Skip to content

Commit

Permalink
Allow the Swift driver to be overridden by a command line flag.
Browse files Browse the repository at this point in the history
This will not work with Bazel until repository-prefixed command line flags are working (bazelbuild/bazel#9177).

RELNOTES: None.
PiperOrigin-RevId: 281381965
  • Loading branch information
allevato authored and swiple-rules-gardener committed Nov 22, 2019
1 parent f51e689 commit a03484c
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 73 deletions.
14 changes: 14 additions & 0 deletions swift/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
)
34 changes: 34 additions & 0 deletions swift/internal/attrs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
),
),
}
73 changes: 33 additions & 40 deletions swift/internal/swift_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ 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",
"SWIFT_FEATURE_MODULE_MAP_HOME_IS_CWD",
"features_for_build_modes",
)
load(":providers.bzl", "SwiftToolchainInfo")
load(":utils.bzl", "get_swift_executable_for_toolchain")

def _default_linker_opts(
cc_toolchain,
Expand Down Expand Up @@ -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)

Expand All @@ -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(),
Expand All @@ -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"],
Expand Down
38 changes: 38 additions & 0 deletions swift/internal/utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
59 changes: 26 additions & 33 deletions swift/internal/xcode_swift_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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.
Expand Down Expand Up @@ -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=<id>` 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")

Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit a03484c

Please sign in to comment.