From e7ce2d13936a1fe234317f228ef7194f27f8520a Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Fri, 22 Nov 2019 10:24:10 -0800 Subject: [PATCH] Allow the Swift driver to be overridden by a command line flag. This will not work with Bazel until repository-prefixed command line flags are working (https://github.com/bazelbuild/bazel/issues/9177). RELNOTES: None. PiperOrigin-RevId: 281994082 --- swift/BUILD | 14 +++++ swift/internal/attrs.bzl | 34 +++++++++++ swift/internal/swift_toolchain.bzl | 73 +++++++++++------------- swift/internal/utils.bzl | 38 ++++++++++++ swift/internal/xcode_swift_toolchain.bzl | 59 +++++++++---------- 5 files changed, 145 insertions(+), 73 deletions(-) diff --git a/swift/BUILD b/swift/BUILD index 9e93e36c2..8e6e378e8 100644 --- a/swift/BUILD +++ b/swift/BUILD @@ -25,3 +25,17 @@ bool_setting( name = "emit_swiftinterface", build_setting_default = False, ) + +# Allows a user to override the default Swift driver during a build, if the +# toolchain is using the default. +label_flag( + name = "default_swift_executable", + build_setting_default = ":empty", +) + +# Empty filegroup used as the default value for `:default_swift_executable` +# since the `build_setting_default` value is required. +filegroup( + name = "empty", + visibility = ["//visibility:private"], +) diff --git a/swift/internal/attrs.bzl b/swift/internal/attrs.bzl index 4b7f121b9..e30b9d773 100644 --- a/swift/internal/attrs.bzl +++ b/swift/internal/attrs.bzl @@ -79,3 +79,37 @@ Linux), those dependencies will be **ignored.** ], **kwargs ) + +def swift_toolchain_driver_attrs(): + """Returns attributes used to attach custom drivers to toolchains. + + These attributes are useful for compiler development alongside Bazel. The + public attribute (`swift_executable`) lets a custom driver be permanently + associated with a particular toolchain instance. If not specified, the + private default is associated with a command-line option that can be used to + provide a custom driver at build time. + + Returns: + A dictionary of attributes that should be added to a toolchain rule. + """ + return { + "swift_executable": attr.label( + allow_single_file = True, + cfg = "host", + doc = """\ +A replacement Swift driver executable. + +If this is empty, the default Swift driver in the toolchain will be used. +Otherwise, this binary will be used and `--driver-mode` will be passed to ensure +that it is invoked in the correct mode (i.e., `swift`, `swiftc`, +`swift-autolink-extract`, etc.). +""", + ), + "_default_swift_executable": attr.label( + allow_files = True, + cfg = "host", + default = Label( + "@build_bazel_rules_swift//swift:default_swift_executable", + ), + ), + } diff --git a/swift/internal/swift_toolchain.bzl b/swift/internal/swift_toolchain.bzl index 31ad49399..7e511de2c 100644 --- a/swift/internal/swift_toolchain.bzl +++ b/swift/internal/swift_toolchain.bzl @@ -22,6 +22,7 @@ toolchain, see `swift.bzl`. load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:partial.bzl", "partial") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load(":attrs.bzl", "swift_toolchain_driver_attrs") load( ":features.bzl", "SWIFT_FEATURE_AUTOLINK_EXTRACT", @@ -29,6 +30,7 @@ load( "features_for_build_modes", ) load(":providers.bzl", "SwiftToolchainInfo") +load(":utils.bzl", "get_swift_executable_for_toolchain") def _default_linker_opts( cc_toolchain, @@ -106,8 +108,11 @@ def _swift_toolchain_impl(ctx): requested_features.extend(ctx.features) requested_features.append(SWIFT_FEATURE_AUTOLINK_EXTRACT) + # Swift.org toolchains assume everything is just available on the PATH so we + # we don't pass any files unless we have a custom driver executable in the + # workspace. all_files = [] - swift_executable = ctx.file.swift_executable + swift_executable = get_swift_executable_for_toolchain(ctx) if swift_executable: all_files.append(swift_executable) @@ -117,8 +122,6 @@ def _swift_toolchain_impl(ctx): return [ SwiftToolchainInfo( action_environment = {}, - # Swift.org toolchains assume everything is just available on the - # PATH and we don't try to pass the toolchain contents here. all_files = depset(all_files), cc_toolchain_info = cc_toolchain, command_line_copts = ctx.fragments.swift.copts(), @@ -143,59 +146,49 @@ def _swift_toolchain_impl(ctx): ] swift_toolchain = rule( - attrs = dicts.add({ - "arch": attr.string( - doc = """\ + attrs = dicts.add( + swift_toolchain_driver_attrs(), + { + "arch": attr.string( + doc = """\ The name of the architecture that this toolchain targets. This name should match the name used in the toolchain's directory layout for architecture-specific content, such as "x86_64" in "lib/swift/linux/x86_64". """, - mandatory = True, - ), - "os": attr.string( - doc = """\ + mandatory = True, + ), + "os": attr.string( + doc = """\ The name of the operating system that this toolchain targets. This name should match the name used in the toolchain's directory layout for platform-specific content, such as "linux" in "lib/swift/linux". """, - mandatory = True, - ), - "root": attr.string( - mandatory = True, - ), - "swift_executable": attr.label( - # TODO(allevato): Use a label-typed build setting to allow this to - # have a default that is overridden from the command line. - allow_single_file = True, - doc = """\ -A replacement Swift driver executable. - -If this is empty, the default Swift driver in the toolchain will be used. -Otherwise, this binary will be used and `--driver-mode` will be passed to ensure -that it is invoked in the correct mode (i.e., `swift`, `swiftc`, -`swift-autolink-extract`, etc.). -""", - ), - "_cc_toolchain": attr.label( - default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), - doc = """\ + mandatory = True, + ), + "root": attr.string( + mandatory = True, + ), + "_cc_toolchain": attr.label( + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), + doc = """\ The C++ toolchain from which other tools needed by the Swift toolchain (such as `clang` and `ar`) will be retrieved. """, - ), - "_worker": attr.label( - cfg = "host", - allow_files = True, - default = Label("//tools/worker"), - doc = """\ + ), + "_worker": attr.label( + cfg = "host", + allow_files = True, + default = Label("//tools/worker"), + doc = """\ An executable that wraps Swift compiler invocations and also provides support for incremental compilation using a persistent mode. """, - executable = True, - ), - }), + executable = True, + ), + }, + ), doc = "Represents a Swift compiler toolchain.", fragments = ["swift"], toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], diff --git a/swift/internal/utils.bzl b/swift/internal/utils.bzl index 516ca034c..0b979476b 100644 --- a/swift/internal/utils.bzl +++ b/swift/internal/utils.bzl @@ -144,6 +144,44 @@ def expand_locations(ctx, values, targets = []): """ return [ctx.expand_location(value, targets) for value in values] +def get_swift_executable_for_toolchain(ctx): + """Returns the Swift driver executable that the toolchain should use. + + Args: + ctx: The toolchain's rule context. + + Returns: + A `File` representing a custom Swift driver executable that the + toolchain should use if provided by the toolchain target or by a command + line option, or `None` if the default driver bundled with the toolchain + should be used. + """ + + # If the toolchain target itself specifies a custom driver, use that. + swift_executable = getattr(ctx.file, "swift_executable", None) + + # If no custom driver was provided by the target, check the value of the + # command-line option and use that if it was provided. + if not swift_executable: + default_swift_executable_files = getattr( + ctx.files, + "_default_swift_executable", + None, + ) + + if default_swift_executable_files: + if len(default_swift_executable_files) > 1: + fail( + "The 'default_swift_executable' option must point to a " + + "single file, but we found {}".format( + str(default_swift_executable_files), + ), + ) + + swift_executable = default_swift_executable_files[0] + + return swift_executable + def get_output_groups(targets, group_name): """Returns files in an output group from each target in a list. diff --git a/swift/internal/xcode_swift_toolchain.bzl b/swift/internal/xcode_swift_toolchain.bzl index fe8f10da6..2221f48a9 100644 --- a/swift/internal/xcode_swift_toolchain.bzl +++ b/swift/internal/xcode_swift_toolchain.bzl @@ -24,6 +24,7 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:partial.bzl", "partial") load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load(":attrs.bzl", "swift_toolchain_driver_attrs") load( ":features.bzl", "SWIFT_FEATURE_AUTOLINK_EXTRACT", @@ -37,6 +38,7 @@ load( "features_for_build_modes", ) load(":providers.bzl", "SwiftToolchainInfo") +load(":utils.bzl", "get_swift_executable_for_toolchain") def _swift_developer_lib_dir(platform_framework_dir): """Returns the directory containing extra Swift developer libraries. @@ -351,7 +353,8 @@ def _xcode_swift_toolchain_impl(ctx): # # To use a "standard" custom toolchain built using the full Swift build # script, use `--define=SWIFT_CUSTOM_TOOLCHAIN=` as shown below. - swift_executable = ctx.file.swift_executable + swift_executable = get_swift_executable_for_toolchain(ctx) + toolchain_root = ctx.var.get("SWIFT_USE_TOOLCHAIN_ROOT") custom_toolchain = ctx.var.get("SWIFT_CUSTOM_TOOLCHAIN") @@ -440,46 +443,36 @@ def _xcode_swift_toolchain_impl(ctx): ] xcode_swift_toolchain = rule( - attrs = dicts.add({ - "swift_executable": attr.label( - # TODO(allevato): Use a label-typed build setting to allow this to - # have a default that is overridden from the command line. - allow_single_file = True, - doc = """\ -A replacement Swift driver executable. - -If this is empty, the default Swift driver in the toolchain will be used. -Otherwise, this binary will be used and `--driver-mode` will be passed to ensure -that it is invoked in the correct mode (i.e., `swift`, `swiftc`, -`swift-autolink-extract`, etc.). -""", - ), - "_cc_toolchain": attr.label( - default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), - doc = """\ + attrs = dicts.add( + swift_toolchain_driver_attrs(), + { + "_cc_toolchain": attr.label( + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), + doc = """\ The C++ toolchain from which linking flags and other tools needed by the Swift toolchain (such as `clang`) will be retrieved. """, - ), - "_worker": attr.label( - cfg = "host", - allow_files = True, - default = Label( - "@build_bazel_rules_swift//tools/worker", ), - doc = """\ + "_worker": attr.label( + cfg = "host", + allow_files = True, + default = Label( + "@build_bazel_rules_swift//tools/worker", + ), + doc = """\ An executable that wraps Swift compiler invocations and also provides support for incremental compilation using a persistent mode. """, - executable = True, - ), - "_xcode_config": attr.label( - default = configuration_field( - name = "xcode_config_label", - fragment = "apple", + executable = True, ), - ), - }), + "_xcode_config": attr.label( + default = configuration_field( + name = "xcode_config_label", + fragment = "apple", + ), + ), + }, + ), doc = "Represents a Swift compiler toolchain provided by Xcode.", fragments = [ "apple",