From c2dc8e3d0748597e2c964a48aaeb9c20426f618f Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 26 Apr 2021 12:17:32 -0700 Subject: [PATCH 1/2] Always build (and use) the new SwiftDriver as the default compiler driver. This will make sure that compiler developers are using the new driver when they build the compiler locally and use it. - Adds a new build-script product category: before_build_script_impl for products we wish to build before the impl products. - Adds a new EarlySwiftDriver product to that category, which gets built with the host toolchain. - Adds an escape hatch: --skip-early-swift-driver - Adjusts the swift CMake configuration with an additional step: swift_create_early_driver_symlinks which (if one was built) creates symlinks in the swift build bin directory to the EarlySwiftDriver swift-driver and swift-help executables. - Adds a new test subset : only_early_swiftdriver, which will get built into a corresponding CMake test target: check-swift-only_early_swiftdriver-* which runs a small subset of driver-related tests against the Early SwiftDriver. - This subset is run always when the compiler itself is tested (--test is specified) - With an escape disable-switch: --skip-test-early-swift-driver - All tests outside of only_early_swiftdriver are forced to run using the legacy C++ driver (to ensure it gets tested, still). NOTE: SwiftDriver product (no 'Early') is still the main product used to build the driver for toolchain installation into and for executing the product's own tests. This change does not affect that. --- cmake/modules/SwiftUtils.cmake | 29 ++++ test/CMakeLists.txt | 2 + test/Unit/lit.cfg | 2 +- test/lit.cfg | 11 ++ tools/driver/CMakeLists.txt | 4 + utils/build-script | 102 ++++++++----- .../build_swift/driver_arguments.py | 15 +- .../build_swift/test_driver_arguments.py | 8 + utils/build_swift/tests/expected_options.py | 9 +- utils/run-test | 1 + .../host_specific_configuration.py | 9 ++ .../swift_build_support/products/__init__.py | 2 + .../products/benchmarks.py | 4 + .../swift_build_support/products/cmark.py | 8 + .../products/earlyswiftdriver.py | 140 ++++++++++++++++++ .../products/foundation.py | 8 + .../products/indexstoredb.py | 4 + .../swift_build_support/products/libcxx.py | 8 + .../products/libdispatch.py | 8 + .../swift_build_support/products/libicu.py | 8 + .../swift_build_support/products/llbuild.py | 8 + .../swift_build_support/products/lldb.py | 8 + .../swift_build_support/products/llvm.py | 8 + .../swift_build_support/products/ninja.py | 4 + .../products/playgroundsupport.py | 4 + .../swift_build_support/products/product.py | 11 ++ .../products/skstresstester.py | 4 + .../products/sourcekitlsp.py | 4 + .../swift_build_support/products/swift.py | 4 + .../products/swiftdriver.py | 8 + .../products/swiftformat.py | 4 + .../products/swiftinspect.py | 4 + .../swift_build_support/products/swiftpm.py | 4 + .../products/swiftsyntax.py | 4 + .../products/tsan_libdispatch.py | 4 + .../swift_build_support/products/xctest.py | 4 + .../infer_dumps_deps_if_verbose_build.test | 2 +- .../infer_implies_install_all.test | 2 +- .../test_early_swift_driver_and_test.test | 8 + .../test_skip_early_swift_driver.test | 7 + 40 files changed, 447 insertions(+), 41 deletions(-) create mode 100644 utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py create mode 100644 validation-test/BuildSystem/test_early_swift_driver_and_test.test create mode 100644 validation-test/BuildSystem/test_skip_early_swift_driver.test diff --git a/cmake/modules/SwiftUtils.cmake b/cmake/modules/SwiftUtils.cmake index 19f17223ec3bc..d6573db26cd7f 100644 --- a/cmake/modules/SwiftUtils.cmake +++ b/cmake/modules/SwiftUtils.cmake @@ -152,6 +152,35 @@ function(swift_create_post_build_symlink target) COMMENT "${CS_COMMENT}") endfunction() +# Once swift-frontend is built, if the standalone (early) swift-driver has been built, +# we create a `swift-driver` symlink adjacent to the `swift` and `swiftc` executables +# to ensure that `swiftc` forwards to the standalone driver when invoked. +function(swift_create_early_driver_symlinks target) + # Early swift-driver is built adjacent to the compiler (swift build dir) + set(driver_bin_dir "${CMAKE_BINARY_DIR}/../earlyswiftdriver-${SWIFT_HOST_VARIANT}-${SWIFT_HOST_VARIANT_ARCH}/release/bin") + set(swift_bin_dir "${SWIFT_RUNTIME_OUTPUT_INTDIR}") + # If early swift-driver wasn't built, nothing to do here. + if(NOT EXISTS "${driver_bin_dir}/swift-driver" OR NOT EXISTS "${driver_bin_dir}/swift-help") + message(STATUS "Skipping creating early SwiftDriver symlinks - no early SwiftDriver build found.") + return() + endif() + + message(STATUS "Creating early SwiftDriver symlinks.") + message(STATUS "From: ${driver_bin_dir}/swift-driver") + message(STATUS "To: ${swift_bin_dir}/swift-driver") + swift_create_post_build_symlink(swift-frontend + SOURCE "${driver_bin_dir}/swift-driver" + DESTINATION "${swift_bin_dir}/swift-driver" + COMMENT "Creating early SwiftDriver symlinks: swift-driver") + + message(STATUS "From: ${driver_bin_dir}/swift-help") + message(STATUS "To: ${swift_bin_dir}/swift-help") + swift_create_post_build_symlink(swift-frontend + SOURCE "${driver_bin_dir}/swift-help" + DESTINATION "${swift_bin_dir}/swift-help" + COMMENT "Creating early SwiftDriver symlinks: swift-help") +endfunction() + function(dump_swift_vars) set(SWIFT_STDLIB_GLOBAL_CMAKE_CACHE) get_cmake_property(variableNames VARIABLES) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4795f40cd9558..6230e846c3065 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -188,6 +188,7 @@ set(TEST_SUBSETS only_validation only_long only_stress + only_early_swiftdriver ) if(NOT "${COVERAGE_DB}" STREQUAL "") @@ -353,6 +354,7 @@ foreach(SDK ${SWIFT_SDKS}) (test_subset STREQUAL "validation") OR (test_subset STREQUAL "only_long") OR (test_subset STREQUAL "only_stress") OR + (test_subset STREQUAL "only_early_swiftdriver") OR (test_subset STREQUAL "all")) list(APPEND directories "${test_bin_dir}") endif() diff --git a/test/Unit/lit.cfg b/test/Unit/lit.cfg index 99713d42a7e95..3d6cf864b8427 100644 --- a/test/Unit/lit.cfg +++ b/test/Unit/lit.cfg @@ -63,7 +63,7 @@ elif swift_test_subset == 'only_stress': # Currently those tests are very fast so it doesn't matter much. pass else: - lit_config.fatal("Unknown test mode %r" % swift_test_subset) + lit_config.fatal("Unknown test subset %r" % swift_test_subset) # test_source_root: The root path where tests are located. # test_exec_root: The root path where tests should be run. diff --git a/test/lit.cfg b/test/lit.cfg index e8171829a1446..6788245aa689a 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -631,6 +631,10 @@ elif swift_test_subset == 'only_stress': config.available_features.add("stress_test") config.limit_to_features.add("stress_test") config.limit_to_features.discard("executable_test") +elif swift_test_subset == 'only_early_swiftdriver': + # Point this subset at a driver-specific set of tests. These are the known reduced subset + # of tests to verify the basic functionality of the standalone (early) swift-driver. + config.test_source_root = os.path.join(config.test_source_root, 'Driver', 'Dependencies') else: lit_config.fatal("Unknown test mode %r" % swift_test_subset) @@ -640,6 +644,13 @@ if 'swift_evolve' in lit_config.params: if not 'swift_driver' in lit_config.params: config.available_features.add("cplusplus_driver") +# Check if we need to run lit tests using the legacy driver or the new driver +# The default for existing test runs is to use the legacy driver. +# The new driver is tested separately. +if swift_test_subset != 'only_early_swiftdriver' and\ + os.environ.get('SWIFT_FORCE_TEST_NEW_DRIVER') is None: + config.environment['SWIFT_USE_OLD_DRIVER'] = '1' + # Enable benchmark testing when the binary is found (has fully qualified path). if config.benchmark_o != 'Benchmark_O': config.available_features.add('benchmark') diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 41ed86909a54f..473c24c295f93 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -16,6 +16,10 @@ target_link_libraries(swift-frontend swiftSymbolGraphGen LLVMBitstreamReader) +# Create a `swift-driver` symlinks adjacent to the `swift-frontend` executable +# to ensure that `swiftc` forwards to the standalone driver when invoked. +swift_create_early_driver_symlinks(swift-frontend) + swift_create_post_build_symlink(swift-frontend SOURCE "swift-frontend${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION "swift${CMAKE_EXECUTABLE_SUFFIX}" diff --git a/utils/build-script b/utils/build-script index 5525617989a70..0f4e162e9d56b 100755 --- a/utils/build-script +++ b/utils/build-script @@ -848,14 +848,24 @@ class BuildScriptInvocation(object): return options def compute_product_classes(self): - """compute_product_classes() -> (list, list) - - Compute the list first of all build-script-impl products and then all - non-build-script-impl products. It is assumed that concatenating the two - lists together will result in a valid dependency graph for the - compilation. + """compute_product_classes() -> (list, list, list) + Compute the list first of all pre-build-script-impl products, then all + build-script-impl products and then all non-build-script-impl products. + It is assumed that concatenating the three lists together will result in a + valid dependency graph for the compilation. """ + before_impl_product_classes = [] + # If --skip-early-swift-driver is passed in, swift will be built + # as usual, but relying on its own C++-based (Legacy) driver. + # Otherwise, we build an "early" swift-driver using the host + # toolchain, which the later-built compiler will forward + # `swiftc` invocations to. That is, if we find a Swift compiler + # in the host toolchain. If the host toolchain is not equpipped with + # a Swift compiler, a warning is emitted. In the future, it may become + # mandatory that the host toolchain come with its own `swiftc`. + if self.args.build_early_swift_driver: + before_impl_product_classes.append(products.EarlySwiftDriver) # FIXME: This is a weird division (returning a list of class objects), # but it matches the existing structure of the `build-script-impl`. @@ -940,6 +950,7 @@ class BuildScriptInvocation(object): # non-build-script-impl products. Otherwise, it would be unsafe to # re-order build-script-impl products in front of non # build-script-impl products. + before_impl_product_classes = [] impl_product_classes = [] product_classes = [] is_darwin = platform.system() == 'Darwin' @@ -950,15 +961,19 @@ class BuildScriptInvocation(object): if p.is_build_script_impl_product(): impl_product_classes.append(p) + elif p.is_before_build_script_impl_product(): + before_impl_product_classes.append(p) else: product_classes.append(p) if self.args.verbose_build: print("Final Build Order:") + for p in before_impl_product_classes: + print(" {}".format(p.product_name())) for p in impl_product_classes: print(" {}".format(p.product_name())) for p in product_classes: print(" {}".format(p.product_name())) - return (impl_product_classes, product_classes) + return (before_impl_product_classes, impl_product_classes, product_classes) def execute(self): """Execute the invocation with the configured arguments.""" @@ -975,7 +990,6 @@ class BuildScriptInvocation(object): return # Otherwise, we compute and execute the individual actions ourselves. - # Compute the list of hosts to operate on. all_host_names = [ self.args.host_target] + self.args.cross_compile_hosts @@ -986,10 +1000,22 @@ class BuildScriptInvocation(object): # # FIXME: This should really be per-host, but the current structure # matches that of `build-script-impl`. - (impl_product_classes, product_classes) = self.compute_product_classes() + (before_impl_product_classes, impl_product_classes, product_classes) =\ + self.compute_product_classes() # Execute each "pass". + # Pre-build-script-impl products... + # Note: currently only supports building for the host. + for host_target in [self.args.host_target]: + for product_class in before_impl_product_classes: + if product_class.is_build_script_impl_product(): + continue + if not product_class.is_before_build_script_impl_product(): + continue + # Execute clean, build, test, install + self.execute_product_build_steps(product_class, host_target) + # Build... for host_target in all_hosts: # FIXME: We should only compute these once. @@ -1029,33 +1055,8 @@ class BuildScriptInvocation(object): for product_class in product_classes: if product_class.is_build_script_impl_product(): continue - product_source = product_class.product_source_name() - product_name = product_class.product_name() - if product_class.is_swiftpm_unified_build_product(): - build_dir = self.workspace.swiftpm_unified_build_dir( - host_target) - else: - build_dir = self.workspace.build_dir( - host_target, product_name) - product = product_class( - args=self.args, - toolchain=self.toolchain, - source_dir=self.workspace.source_dir(product_source), - build_dir=build_dir) - if product.should_clean(host_target): - print("--- Cleaning %s ---" % product_name) - product.clean(host_target) - if product.should_build(host_target): - print("--- Building %s ---" % product_name) - product.build(host_target) - if product.should_test(host_target): - print("--- Running tests for %s ---" % product_name) - product.test(host_target) - print("--- Finished tests for %s ---" % product_name) - if product.should_install(host_target) or \ - (self.install_all and product.should_build(host_target)): - print("--- Installing %s ---" % product_name) - product.install(host_target) + # Execute clean, build, test, install + self.execute_product_build_steps(product_class, host_target) # Extract symbols... for host_target in all_hosts: @@ -1103,6 +1104,35 @@ class BuildScriptInvocation(object): ["--only-execute", action_name], env=self.impl_env, echo=self.args.verbose_build) + def execute_product_build_steps(self, product_class, host_target): + product_source = product_class.product_source_name() + product_name = product_class.product_name() + if product_class.is_swiftpm_unified_build_product(): + build_dir = self.workspace.swiftpm_unified_build_dir( + host_target) + else: + build_dir = self.workspace.build_dir( + host_target, product_name) + product = product_class( + args=self.args, + toolchain=self.toolchain, + source_dir=self.workspace.source_dir(product_source), + build_dir=build_dir) + if product.should_clean(host_target): + print("--- Cleaning %s ---" % product_name) + product.clean(host_target) + if product.should_build(host_target): + print("--- Building %s ---" % product_name) + product.build(host_target) + if product.should_test(host_target): + print("--- Running tests for %s ---" % product_name) + product.test(host_target) + print("--- Finished tests for %s ---" % product_name) + if product.should_install(host_target) or \ + (self.install_all and product.should_build(host_target)): + print("--- Installing %s ---" % product_name) + product.install(host_target) + # ----------------------------------------------------------------------------- # Main (preset) diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index 126815c1b21c9..e8e79b443d3a8 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -193,6 +193,12 @@ def _apply_default_arguments(args): args.test_swiftevolve = False args.test_toolchainbenchmarks = False + # --test implies --test-early-swift-driver + # (unless explicitly skipped with `--skip-test-early-swift-driver`) + if args.test and (args.build_early_swift_driver and + args.test_early_swift_driver is None): + args.test_early_swift_driver = True + # --skip-test-ios is merely a shorthand for host and simulator tests. if not args.test_ios: args.test_ios_host = False @@ -610,6 +616,9 @@ def create_argument_parser(): option(['--swift-driver'], toggle_true('build_swift_driver'), help='build swift-driver') + option(['--skip-early-swift-driver'], toggle_false('build_early_swift_driver'), + help='skip building the early swift-driver') + option(['--indexstore-db'], toggle_true('build_indexstoredb'), help='build IndexStoreDB') option('--test-indexstore-db-sanitize-all', @@ -1036,9 +1045,13 @@ def create_argument_parser(): toggle_false('test_android_host'), help='skip testing Android device targets on the host machine (the ' 'phone itself)') - option('--skip-clean-llbuild', toggle_false('clean_llbuild'), help='skip cleaning up llbuild') + option('--clean-early-swift-driver', toggle_true('clean_early_swift_driver'), + help='Clean up the early SwiftDriver') + option('--skip-test-early-swift-driver', + store('test_early_swift_driver', const=False), + help='Test the early SwiftDriver against the host toolchain') option('--skip-clean-swiftpm', toggle_false('clean_swiftpm'), help='skip cleaning up swiftpm') option('--skip-clean-swift-driver', toggle_false('clean_swift_driver'), diff --git a/utils/build_swift/tests/build_swift/test_driver_arguments.py b/utils/build_swift/tests/build_swift/test_driver_arguments.py index b9c3ad10527cf..370e9f4538be4 100644 --- a/utils/build_swift/tests/build_swift/test_driver_arguments.py +++ b/utils/build_swift/tests/build_swift/test_driver_arguments.py @@ -586,6 +586,14 @@ def test_implied_defaults_test_optimize_for_size(self): namespace = self.parse_default_args(['--test-optimize-for-size']) self.assertTrue(namespace.test) + def test_implied_defaults_test_early_swift_driver(self): + namespace = self.parse_default_args(['--test']) + self.assertTrue(namespace.test_early_swift_driver) + + def test_implied_defaults_test_no_early_swift_driver(self): + namespace = self.parse_default_args(['--test --skip-early-swift-driver']) + self.assertTrue(namespace.test_early_swift_driver is None) + def test_implied_defaults_test_optimize_none_with_implicit_dynamic(self): namespace = self.parse_default_args( ['--test-optimize-none-with-implicit-dynamic']) diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index df858ebbb891f..57ceb9bf9129a 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -89,6 +89,7 @@ 'build_swift_stdlib_unittest_extra': False, 'build_swiftpm': False, 'build_swift_driver': False, + 'build_early_swift_driver': True, 'build_swiftsyntax': False, 'build_libparser_only': False, 'build_skstresstester': False, @@ -99,8 +100,8 @@ 'test_sourcekitlsp_sanitize_all': False, 'build_sourcekitlsp': False, 'install_swiftpm': False, - 'install_swift_driver': False, 'install_swiftsyntax': False, + 'install_swift_driver': False, 'swiftsyntax_verify_generated_files': False, 'install_playgroundsupport': False, 'install_sourcekitlsp': False, @@ -215,7 +216,9 @@ 'clean_llbuild': True, 'clean_swiftpm': True, 'clean_swift_driver': True, + 'clean_early_swift_driver': False, 'test': None, + 'test_early_swift_driver': None, 'test_android': False, 'test_android_host': False, 'test_cygwin': False, @@ -461,6 +464,8 @@ class BuildScriptImplOption(_BaseOption): SetOption('--skip-ios', dest='ios', value=False), SetOption('--skip-tvos', dest='tvos', value=False), SetOption('--skip-watchos', dest='watchos', value=False), + SetOption('--skip-test-early-swift-driver', + dest='test_early_swift_driver', value=False), SetTrueOption('--benchmark'), SetTrueOption('--clean'), @@ -555,6 +560,7 @@ class BuildScriptImplOption(_BaseOption): EnableOption('--watchos'), EnableOption('--xctest', dest='build_xctest'), EnableOption('--swift-disable-dead-stripping'), + EnableOption('--clean-early-swift-driver', dest='clean_early_swift_driver'), DisableOption('--skip-build-cmark', dest='build_cmark'), DisableOption('--skip-build-llvm', dest='build_llvm'), @@ -580,6 +586,7 @@ class BuildScriptImplOption(_BaseOption): DisableOption('--skip-build-watchos-simulator', dest='build_watchos_simulator'), DisableOption('--skip-clean-llbuild', dest='clean_llbuild'), + DisableOption('--skip-early-swift-driver', dest='build_early_swift_driver'), DisableOption('--skip-clean-swiftpm', dest='clean_swiftpm'), DisableOption('--skip-clean-swift-driver', dest='clean_swift_driver'), DisableOption('--skip-test-android', dest='test_android'), diff --git a/utils/run-test b/utils/run-test index 780383604c85e..9e1fafab51e0f 100755 --- a/utils/run-test +++ b/utils/run-test @@ -37,6 +37,7 @@ TEST_SUBSETS = [ 'only_validation', 'only_long', 'only_stress', + 'only_early_swiftdriver' ] SWIFT_SOURCE_DIR = os.path.join(SWIFT_SOURCE_ROOT, 'swift') diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index 9bd750657b8f6..9ab0ea6921cfc 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -167,6 +167,14 @@ def __init__(self, host_target, args): else: subset_suffix = "" + # If the compiler is being tested after being built to use the + # standalone swift-driver, we build a test-target to + # run a reduced set of lit-tests that verify the early swift-driver. + if getattr(args, 'test_early_swift_driver', False) and\ + not test_host_only: + self.swift_test_run_targets.append( + "check-swift-only_early_swiftdriver-{}".format(name)) + # Support for running the macCatalyst tests with # the iOS-like target triple. macosx_platform_match = re.search("macosx-(.*)", name) @@ -180,6 +188,7 @@ def __init__(self, host_target, args): (self.swift_test_run_targets .append("check-swift{}{}-{}".format( subset_suffix, suffix, name))) + if args.test_optimized and not test_host_only: self.swift_test_run_targets.append( "check-swift{}-optimize-{}".format( diff --git a/utils/swift_build_support/swift_build_support/products/__init__.py b/utils/swift_build_support/swift_build_support/products/__init__.py index 169fe506eeb0e..c13c0dc4b5491 100644 --- a/utils/swift_build_support/swift_build_support/products/__init__.py +++ b/utils/swift_build_support/swift_build_support/products/__init__.py @@ -12,6 +12,7 @@ from .benchmarks import Benchmarks from .cmark import CMark +from .earlyswiftdriver import EarlySwiftDriver from .foundation import Foundation from .indexstoredb import IndexStoreDB from .libcxx import LibCXX @@ -51,6 +52,7 @@ 'SwiftInspect', 'SwiftPM', 'SwiftDriver', + 'EarlySwiftDriver', 'XCTest', 'SwiftSyntax', 'SKStressTester', diff --git a/utils/swift_build_support/swift_build_support/products/benchmarks.py b/utils/swift_build_support/swift_build_support/products/benchmarks.py index b7a14ecc658fa..0832299b8aec2 100644 --- a/utils/swift_build_support/swift_build_support/products/benchmarks.py +++ b/utils/swift_build_support/swift_build_support/products/benchmarks.py @@ -38,6 +38,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/cmark.py b/utils/swift_build_support/swift_build_support/products/cmark.py index cac4f2e03d1b9..b55dcaa52fbdf 100644 --- a/utils/swift_build_support/swift_build_support/products/cmark.py +++ b/utils/swift_build_support/swift_build_support/products/cmark.py @@ -22,6 +22,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + # This is the root of the build-graph, so it doesn't have any dependencies. @classmethod def get_dependencies(cls): diff --git a/utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py b/utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py new file mode 100644 index 0000000000000..9fad3c92f52b3 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/earlyswiftdriver.py @@ -0,0 +1,140 @@ +# swift_build_support/products/swiftdriver.py -------------------*- python -*- +# +# 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 os + +from . import product +from .. import shell +from .. import toolchain + + +# SwiftDriver is a standalone compiler-driver application written in +# Swift. This build product is a "Special" SwiftDriver that gets built +# with the host toolchain to act as *the* compiler driver for the +# `swift` build directory compiler, hence it does not depend on any other +# build product of `build-script`. +# +# Today, if the host toolchain is not equipped with Swift, or the user +# explicitly opts out of using SwiftDriver (`-skip-early-swiftdriver`) +# (relying on a fallback to the legacy driver), a warning is emitted. +# In the future, a Swift-equipped host toolchain may become mandatory. +class EarlySwiftDriver(product.Product): + @classmethod + def product_source_name(cls): + return "swift-driver" + + @classmethod + def is_build_script_impl_product(cls): + return False + + @classmethod + def is_before_build_script_impl_product(cls): + return True + + def should_build(self, host_target): + if self.args.build_early_swift_driver: + if toolchain.host_toolchain().find_tool("swift") is None: + warn_msg = 'Host toolchain could not locate a '\ + 'compiler to build swift-drver. '\ + '(Try `--skip-early-swift-driver`)' + print('-- Warning: {}', warn_msg) + return False + else: + return True + return False + + @classmethod + def get_dependencies(cls): + return [] + + def should_clean(self, host_target): + return self.args.clean_early_swift_driver + + def clean(self, host_target): + run_build_script_helper('clean', host_target, self, self.args) + + def build(self, host_target): + run_build_script_helper('build', host_target, self, self.args) + + def should_test(self, host_target): + # EarlySwiftDriver is tested against the compiler's lit test + # suite driver subset, as a standalone test CMake target when + # `swift` is built. We do not run the driver's own tests here. + return False + + def test(self, host_target): + run_build_script_helper('test', host_target, self, self.args) + + def should_install(self, host_target): + # This product is for the swift-driver used with the build-directory compiler. + # If a toolchain install is required, please use the SwiftDriver (no 'Early') + # product with `--swift-driver --install-swift-driver`. + return False + + def install(self, host_target): + run_build_script_helper('install', host_target, self, self.args) + + +def run_build_script_helper(action, host_target, product, args): + build_root = os.path.dirname(product.build_dir) + script_path = os.path.join( + product.source_dir, 'Utilities', 'build-script-helper.py') + + # Building with the host toolchain for use with a local compiler build, + # use the toolchain which is supplying the `swiftc`. + swiftc_path = os.path.abspath(product.toolchain.swiftc) + toolchain_path = os.path.dirname(os.path.dirname(swiftc_path)) + + # Pass Dispatch directory down if we built it + dispatch_build_dir = os.path.join( + build_root, '%s-%s' % ('libdispatch', host_target)) + + # Pass Foundation directory down if we built it + foundation_build_dir = os.path.join( + build_root, '%s-%s' % ('foundation', host_target)) + + # Pass the swift lit tests if we're testing and the Swift tests were built + swift_build_dir = os.path.join( + build_root, 'swift-{}'.format(host_target)) + lit_test_dir = os.path.join( + swift_build_dir, 'test-{}'.format(host_target)) + + is_release = product.is_release() + configuration = 'release' if is_release else 'debug' + helper_cmd = [ + script_path, + action, + '--package-path', product.source_dir, + '--build-path', product.build_dir, + '--configuration', configuration, + '--toolchain', toolchain_path, + '--ninja-bin', product.toolchain.ninja, + '--cmake-bin', product.toolchain.cmake, + '--local_compiler_build' + ] + + if os.path.exists(dispatch_build_dir): + helper_cmd += [ + '--dispatch-build-dir', dispatch_build_dir + ] + if os.path.exists(foundation_build_dir): + helper_cmd += [ + '--foundation-build-dir', foundation_build_dir + ] + if os.path.exists(lit_test_dir) and action == 'test': + helper_cmd += [ + '--lit-test-dir', lit_test_dir + ] + if args.verbose_build: + helper_cmd.append('--verbose') + + shell.call(helper_cmd) diff --git a/utils/swift_build_support/swift_build_support/products/foundation.py b/utils/swift_build_support/swift_build_support/products/foundation.py index 99a362ac82693..79d843b31b1ec 100644 --- a/utils/swift_build_support/swift_build_support/products/foundation.py +++ b/utils/swift_build_support/swift_build_support/products/foundation.py @@ -28,6 +28,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/products/indexstoredb.py b/utils/swift_build_support/swift_build_support/products/indexstoredb.py index 83b4ac91811c4..28838a443e8dd 100644 --- a/utils/swift_build_support/swift_build_support/products/indexstoredb.py +++ b/utils/swift_build_support/swift_build_support/products/indexstoredb.py @@ -37,6 +37,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/libcxx.py b/utils/swift_build_support/swift_build_support/products/libcxx.py index d26b8bda83cf7..0103f8cd294ee 100644 --- a/utils/swift_build_support/swift_build_support/products/libcxx.py +++ b/utils/swift_build_support/swift_build_support/products/libcxx.py @@ -24,6 +24,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def get_dependencies(cls): return [cmark.CMark, diff --git a/utils/swift_build_support/swift_build_support/products/libdispatch.py b/utils/swift_build_support/swift_build_support/products/libdispatch.py index 62bf2475948d5..d5ff58a826ad2 100644 --- a/utils/swift_build_support/swift_build_support/products/libdispatch.py +++ b/utils/swift_build_support/swift_build_support/products/libdispatch.py @@ -27,6 +27,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/products/libicu.py b/utils/swift_build_support/swift_build_support/products/libicu.py index 39e1b60262f70..038ab9f6eb8b7 100644 --- a/utils/swift_build_support/swift_build_support/products/libicu.py +++ b/utils/swift_build_support/swift_build_support/products/libicu.py @@ -25,6 +25,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/utils/swift_build_support/swift_build_support/products/llbuild.py b/utils/swift_build_support/swift_build_support/products/llbuild.py index e723b03ecb199..b622379c211d2 100644 --- a/utils/swift_build_support/swift_build_support/products/llbuild.py +++ b/utils/swift_build_support/swift_build_support/products/llbuild.py @@ -30,6 +30,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def get_dependencies(cls): return [cmark.CMark, diff --git a/utils/swift_build_support/swift_build_support/products/lldb.py b/utils/swift_build_support/swift_build_support/products/lldb.py index 73a9017bbe407..be6004740c77b 100644 --- a/utils/swift_build_support/swift_build_support/products/lldb.py +++ b/utils/swift_build_support/swift_build_support/products/lldb.py @@ -27,6 +27,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @classmethod def get_dependencies(cls): return [cmark.CMark, diff --git a/utils/swift_build_support/swift_build_support/products/llvm.py b/utils/swift_build_support/swift_build_support/products/llvm.py index 1bc27bc89e38a..7480f14503ce0 100644 --- a/utils/swift_build_support/swift_build_support/products/llvm.py +++ b/utils/swift_build_support/swift_build_support/products/llvm.py @@ -43,6 +43,14 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + """ + return False + @property def _compiler_vendor_flags(self): if self.args.compiler_vendor == "none": diff --git a/utils/swift_build_support/swift_build_support/products/ninja.py b/utils/swift_build_support/swift_build_support/products/ninja.py index fecd25adc8d27..722fb61b3d63d 100644 --- a/utils/swift_build_support/swift_build_support/products/ninja.py +++ b/utils/swift_build_support/swift_build_support/products/ninja.py @@ -30,6 +30,10 @@ class Ninja(product.Product): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def new_builder(cls, args, toolchain, workspace, host): return NinjaBuilder(cls, args, toolchain, workspace) diff --git a/utils/swift_build_support/swift_build_support/products/playgroundsupport.py b/utils/swift_build_support/swift_build_support/products/playgroundsupport.py index 0e98b442b14be..90d7c16116fa3 100644 --- a/utils/swift_build_support/swift_build_support/products/playgroundsupport.py +++ b/utils/swift_build_support/swift_build_support/products/playgroundsupport.py @@ -46,6 +46,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return self.args.build_playgroundsupport diff --git a/utils/swift_build_support/swift_build_support/products/product.py b/utils/swift_build_support/swift_build_support/products/product.py index 075b934d10ac1..7e072d3d1f969 100644 --- a/utils/swift_build_support/swift_build_support/products/product.py +++ b/utils/swift_build_support/swift_build_support/products/product.py @@ -58,6 +58,17 @@ def is_build_script_impl_product(cls): """ raise NotImplementedError + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is build before any build-script-impl products. + Such products must be non-build_script_impl products. + Because such products are built ahead of the compiler, they are + built using the host toolchain. + """ + raise NotImplementedError + @classmethod def is_swiftpm_unified_build_product(cls): """is_swiftpm_unified_build_product -> bool diff --git a/utils/swift_build_support/swift_build_support/products/skstresstester.py b/utils/swift_build_support/swift_build_support/products/skstresstester.py index 0656a2bfd7d3a..5e753a5624b8f 100644 --- a/utils/swift_build_support/swift_build_support/products/skstresstester.py +++ b/utils/swift_build_support/swift_build_support/products/skstresstester.py @@ -43,6 +43,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def is_swiftpm_unified_build_product(cls): return True diff --git a/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py b/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py index aa3d22602aeac..c7349ab7e60de 100644 --- a/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py +++ b/utils/swift_build_support/swift_build_support/products/sourcekitlsp.py @@ -33,6 +33,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/swift.py b/utils/swift_build_support/swift_build_support/products/swift.py index 1e4b4feb2df9b..661df9103ac65 100644 --- a/utils/swift_build_support/swift_build_support/products/swift.py +++ b/utils/swift_build_support/swift_build_support/products/swift.py @@ -60,6 +60,10 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + return False + @property def _runtime_sanitizer_flags(self): sanitizer_list = [] diff --git a/utils/swift_build_support/swift_build_support/products/swiftdriver.py b/utils/swift_build_support/swift_build_support/products/swiftdriver.py index 4f2bd72c3f127..a85e71fba4b34 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftdriver.py +++ b/utils/swift_build_support/swift_build_support/products/swiftdriver.py @@ -27,6 +27,10 @@ from .. import targets +# SwiftDriver is a standalone compiler-driver application written in +# Swift. This build product is *the* driver product that is +# installed into a resulting toolchain. It is built-with and depends-on +# other build products of this build (compiler, package-manager, etc). class SwiftDriver(product.Product): @classmethod def product_source_name(cls): @@ -36,6 +40,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return self.args.build_swift_driver diff --git a/utils/swift_build_support/swift_build_support/products/swiftformat.py b/utils/swift_build_support/swift_build_support/products/swiftformat.py index 940ccd1043e6c..42769f1ea388a 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftformat.py +++ b/utils/swift_build_support/swift_build_support/products/swiftformat.py @@ -42,6 +42,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def is_swiftpm_unified_build_product(cls): return True diff --git a/utils/swift_build_support/swift_build_support/products/swiftinspect.py b/utils/swift_build_support/swift_build_support/products/swiftinspect.py index b2c54190f1b1e..6bf49f213f61c 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftinspect.py +++ b/utils/swift_build_support/swift_build_support/products/swiftinspect.py @@ -38,6 +38,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/swiftpm.py b/utils/swift_build_support/swift_build_support/products/swiftpm.py index 25e982e23f173..4a97f377ef408 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftpm.py +++ b/utils/swift_build_support/swift_build_support/products/swiftpm.py @@ -34,6 +34,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/swiftsyntax.py b/utils/swift_build_support/swift_build_support/products/swiftsyntax.py index 00cdef0dd0c5c..f32c453aad37f 100644 --- a/utils/swift_build_support/swift_build_support/products/swiftsyntax.py +++ b/utils/swift_build_support/swift_build_support/products/swiftsyntax.py @@ -41,6 +41,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def is_swiftpm_unified_build_product(cls): return True diff --git a/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py b/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py index 09b8eccaa8d1a..c30b938147eaf 100644 --- a/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py +++ b/utils/swift_build_support/swift_build_support/products/tsan_libdispatch.py @@ -39,6 +39,10 @@ def product_source_name(cls): def is_build_script_impl_product(cls): return False + @classmethod + def is_before_build_script_impl_product(cls): + return False + def should_build(self, host_target): return True diff --git a/utils/swift_build_support/swift_build_support/products/xctest.py b/utils/swift_build_support/swift_build_support/products/xctest.py index 56eda1461a72c..97278cbeceee8 100644 --- a/utils/swift_build_support/swift_build_support/products/xctest.py +++ b/utils/swift_build_support/swift_build_support/products/xctest.py @@ -29,6 +29,10 @@ def is_build_script_impl_product(cls): """ return True + @classmethod + def is_before_build_script_impl_product(cls): + return False + @classmethod def product_source_name(cls): """product_source_name() -> str diff --git a/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test b/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test index 16fc073567452..6b0a58687d397 100644 --- a/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test +++ b/validation-test/BuildSystem/infer_dumps_deps_if_verbose_build.test @@ -1,6 +1,6 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --verbose-build --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build diff --git a/validation-test/BuildSystem/infer_implies_install_all.test b/validation-test/BuildSystem/infer_implies_install_all.test index 48b570c66ec12..16fee74e3d318 100644 --- a/validation-test/BuildSystem/infer_implies_install_all.test +++ b/validation-test/BuildSystem/infer_implies_install_all.test @@ -1,6 +1,6 @@ # RUN: %empty-directory(%t) # RUN: mkdir -p %t -# RUN: SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --infer --swiftpm --cmake %cmake 2>&1 | %FileCheck %s # REQUIRES: standalone_build diff --git a/validation-test/BuildSystem/test_early_swift_driver_and_test.test b/validation-test/BuildSystem/test_early_swift_driver_and_test.test new file mode 100644 index 0000000000000..e9eb63a766131 --- /dev/null +++ b/validation-test/BuildSystem/test_early_swift_driver_and_test.test @@ -0,0 +1,8 @@ +# REQUIRES: standalone_build + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --test --cmake %cmake 2>&1 | %FileCheck %s + +# CHECK: --- Building earlyswiftdriver --- +# CHECK: cmake --build {{.*}}check-swift-only_early_swiftdriver \ No newline at end of file diff --git a/validation-test/BuildSystem/test_skip_early_swift_driver.test b/validation-test/BuildSystem/test_skip_early_swift_driver.test new file mode 100644 index 0000000000000..532d7444176e4 --- /dev/null +++ b/validation-test/BuildSystem/test_skip_early_swift_driver.test @@ -0,0 +1,7 @@ +# REQUIRES: standalone_build + +# RUN: %empty-directory(%t) +# RUN: mkdir -p %t +# RUN: SKIP_XCODE_VERSION_CHECK=1 SWIFT_BUILD_ROOT=%t %swift_src_root/utils/build-script --dry-run --cmake %cmake --skip-early-swift-driver 2>&1 | %FileCheck %s + +# CHECK-NOT: --- Building earlyswiftdriver --- \ No newline at end of file From 60a4af3dcca01f7bac627e22847809536bd6e15e Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 26 Apr 2021 12:17:56 -0700 Subject: [PATCH 2/2] Add documentation about the compiler's standalone driver and its build procedure. --- docs/DebuggingTheCompiler.md | 133 +++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/docs/DebuggingTheCompiler.md b/docs/DebuggingTheCompiler.md index 27bcaf9ca6e28..8fd117afa17a5 100644 --- a/docs/DebuggingTheCompiler.md +++ b/docs/DebuggingTheCompiler.md @@ -36,6 +36,14 @@ benefit of all Swift developers. - [Bisecting on SIL optimizer pass counts to identify optimizer bugs](#bisecting-on-sil-optimizer-pass-counts-to-identify-optimizer-bugs) - [Using git-bisect in the presence of branch forwarding/feature branches](#using-git-bisect-in-the-presence-of-branch-forwardingfeature-branches) - [Reducing SIL test cases using bug_reducer](#reducing-sil-test-cases-using-bug_reducer) +- [Debugging the Compiler Build](#debugging-the-compiler-build) + - [Build Dry Run](#build-dry-run) +- [Debugging the Compiler Driver](#debugging-the-compiler-driver-build) + - [Swift Compiler Driver F.A.Q](#swift-compiler-driver-f.a.q.) + - [Building the compiler without using the standalone driver](#building-the-compiler-without-the-standalone-driver) + - [Invoking the compiler without forwarding to the standalone driver](#invoking-the-compiler-without-forwarding-to-the-standalone-driver) + - [Reproducing the Compiler Driver build steps](#reproducing-the-compiler-driver-build-steps) + - [Installing the Compiler Driver](#installing-the-compiler-driver) - [Debugging Swift Executables](#debugging-swift-executables) - [Determining the mangled name of a function in LLDB](#determining-the-mangled-name-of-a-function-in-lldb) - [Manually symbolication using LLDB](#manually-symbolication-using-lldb) @@ -807,6 +815,131 @@ reducing SIL test cases by: For more information and a high level example, see: ./swift/utils/bug_reducer/README.md. +# Debugging the Compiler Build + +## Build Dry Run + +A "dry-run" invocation of the `build-script` (using the `--dry-run` flag) will +print the commands that would be executed in a given build, without executing +them. A dry-run script invocation output can be used to inspect the build stages +of a given `build-script` configuration, or create script corresponding to one +such configuration. + +# Debugging the Compiler Driver + +The Swift compiler uses a standalone compiler-driver application written in +Swift: [swift-driver](https://github.com/apple/swift-driver). When building the +compiler using `build-script`, by default, the standalone driver will be built +first, using the host toolchain, if the host toolchain contains a Swift +compiler. If the host toolchain does not contain Swift, a warning is emitted and +the legacy compiler-driver (integrated in the C++ code-base) will be used. In +the future, a host toolchain containing a Swift compiler may become mandatory. +Once the compiler is built, the compiler build directory (`swift--`) +is updated with a symlink to the standalone driver, ensuring calls to the build +directory's `swift` and `swiftc` always forward to the standalone driver. + +For more information about the driver, see: +[github.com/apple/swift-driver/blob/main/README.md](https://github.com/apple/swift-driver/blob/main/README.md) + +## Swift Compiler Driver F.A.Q. +> What's the difference between invoking 'swiftc' vs. 'swift-driver' at the top + level? + +Today, `swift` and `swiftc` are symbolic links to the compiler binary +(`swift-frontend`). Invoking `swiftc` causes the executable to detects that it +is a compiler-driver invocation, and not a direct compiler-frontend invocation, +by examining the invoked program's name. The compiler frontend can be invoked +directly by invoking the `swift-frontend` executable, or passing in the +`-frontend` option to `swiftc`. + +The standalone [Compiler Driver](https://github.com/apple/swift-driver) is +installed as a separate `swift-driver` executable in the Swift toolchain's `bin` +directory. When a user launches the compiler by invoking `swiftc`, the C++ based +compiler executable forwards the invocation to the `swift-driver` executable if +one is found alongside it. This forwarding mechanism is in-place temporarily, to +allow for an easy fallback to the legacy driver via one of the two escape +hatches: + +- `-disallow-use-new-driver` command line flag +- `SWIFT_USE_OLD_DRIVER` environment variable + +If the user is to directly invoke the `swift-driver` executable, the behaviour +should be the same as invoking the `swiftc` executable, but without the option +for a legacy driver fallback. + +Once the legacy driver is deprecated, `swift` and `swiftc` executables will +become symbolic links to the `swift-driver` executable directly. + + +> Will 'swiftc ... -###' always print the same set of commands for the old/new + driver? Do they call 'swift-frontend' the same way? + +The standalone [Compiler Driver](https://github.com/apple/swift-driver) is meant +to be a direct drop-in replacement for the C++-based legacy driver. It has the +exact same command-line interface. The expectation is that its behaviour closely +matches the legacy driver; however, during, and after the transition to the new +driver being the default its behaviour may start to diverge from the legacy +driver as par for the course of its evolution and gaining new features, etc. +Today, broadly-speaking, sets of `swift-frontend` invocations generated by the +two drivers are expected to be very similar. + +## Building the compiler without the standalone driver +One can build the compiler that does not rely on the standalone driver and +instead uses the legacy, built-in driver using the `build-script` option: +`--skip-early-swift-driver`. + +## Invoking the compiler without forwarding to the standalone driver +The Swift compiler can currently be invoked in an execution mode that will use +the legacy C++-based compiler driver using one of the following two options: +- Passing `-disallow-use-new-driver` argument to the `swiftc` invocation +- Setting the `SWIFT_USE_OLD_DRIVER` environment variable + +## Reproducing the Compiler Driver build steps +A "[dry-run](#build-dry-run)" invocation of the `build-script` can be used to +examine the SwiftDriver build stage and commands, without executing it. For +example: +``` +$ utils/build-script --release-debuginfo --dry-run ++ mkdir -p /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert +--- Building earlyswiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py build --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/earlyswiftdriver-macosx-x86_64 --configuration release --toolchain /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake --local_compiler_build +Building the standard library for: swift-test-stdlib-macosx-x86_64 +... +``` +One of the first steps is an invocation of the driver's +`build-script-helper.py` script which specifies that the driver us to be built +(`build`) using the host toolchain (`--toolchain`) to a specified location +(`--build-path`). + +## Installing the Compiler Driver +In order to create a Swift compiler installation (`--install-swift`), the +standalone driver must be built as a separate build product using the +*just-built* Swift compiler and toolchain (the ones built in the same +`build-script` invocation, preceeding the SwiftDriver build product). The +additional build product is added to the build by specifying the +`--swift-driver` option of the `build-script`. The driver product is istalled +into the resulting toolchain installation by specifying the +`--install-swift-driver` option of the `build-script`. + +Note, a "dry-run" `build-script` invocation when installing the standalone +driver will demonstrate the commands required to build and install the driver as +a standalone build product: +``` +$ utils/build-script --release-debuginfo --dry-run --swift-driver --install-swift-driver +... +--- Cleaning swiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py clean --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/swiftdriver-macosx-x86_64 --configuration release --toolchain /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/toolchain-macosx-x86_64/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake +--- Building swiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py build --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/swiftdriver-macosx-x86_64 --configuration release --toolchain /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/toolchain-macosx-x86_64/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake +--- Installing swiftdriver --- ++ /SwiftWorkspace/swift-driver/Utilities/build-script-helper.py install --package-path /SwiftWorkspace/swift-driver --build-path /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/swiftdriver-macosx-x +86_64 --configuration release --toolchain /SwiftWorkspace/build/Ninja-RelWithDebInfoAssert/toolchain-macosx-x86_64/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr --ninja-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/ninja --cmake-bin /Applications/Xcode.app/Contents/Developer/usr/local/bin/cmake +``` +These invocations of the driver's `build-script-helper.py` script specify the +individual build actions (`clean`, `build`, `install`), the product build path +(`--build-path`), and the *just-built* toolchain which should be used +(`--toolchain`). + # Debugging Swift Executables One can use the previous tips for debugging the Swift compiler with Swift