Skip to content
This repository has been archived by the owner on Jan 25, 2024. It is now read-only.

Commit

Permalink
Modernize xcode_swift_toolchain features and linkopts.
Browse files Browse the repository at this point in the history
This change adds the search path for StoreKitTest/XCTest now available on watchOS in Xcode 12.5, and retires a number of workarounds and/or conditional code paths that are for older versions of Xcode that we don't care about anymore. For example, statically linking the Swift runtime is no longer supported on Darwin with modern versions of Xcode.

PiperOrigin-RevId: 377920556
  • Loading branch information
allevato authored and swiple-rules-gardener committed Jun 7, 2021
1 parent a5eb357 commit bf5fccc
Showing 1 changed file with 67 additions and 114 deletions.
181 changes: 67 additions & 114 deletions swift/internal/xcode_swift_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -117,45 +117,59 @@ def _command_line_objc_copts(compilation_mode, objc_fragment):
clang_copts = objc_fragment.copts + legacy_copts
return [copt for copt in clang_copts if copt != "-g"]

def _platform_developer_framework_dir(apple_toolchain, apple_fragment):
def _platform_developer_framework_dir(
apple_toolchain,
apple_fragment,
xcode_config):
"""Returns the Developer framework directory for the platform.
Args:
apple_fragment: The `apple` configuration fragment.
apple_toolchain: The `apple_common.apple_toolchain()` object.
xcode_config: The Xcode configuration.
Returns:
The path to the Developer framework directory for the platform if one
exists, otherwise `None`.
"""

# All platforms have a `Developer/Library/Frameworks` directory in their
# platform root, except for watchOS prior to Xcode 12.5.
platform_type = apple_fragment.single_arch_platform.platform_type
if platform_type == apple_common.platform_type.watchos:
if (
platform_type == apple_common.platform_type.watchos and
not _is_xcode_at_least_version(xcode_config, "12.5")
):
return None

# All platforms except watchOS have a `Developer/Library/Frameworks`
# directory in their platform root.
return apple_toolchain.platform_developer_framework_dir(apple_fragment)

def _sdk_developer_framework_dir(apple_toolchain, apple_fragment):
def _sdk_developer_framework_dir(apple_toolchain, apple_fragment, xcode_config):
"""Returns the Developer framework directory for the SDK.
Args:
apple_fragment: The `apple` configuration fragment.
apple_toolchain: The `apple_common.apple_toolchain()` object.
xcode_config: The Xcode configuration.
Returns:
The path to the Developer framework directory for the SDK if one
exists, otherwise `None`.
"""

# All platforms have a `Developer/Library/Frameworks` directory in their SDK
# root except for macOS (all versions of Xcode so far), and watchOS (prior
# to Xcode 12.5).
platform_type = apple_fragment.single_arch_platform.platform_type
if platform_type in (
apple_common.platform_type.macos,
apple_common.platform_type.watchos,
if (
platform_type == apple_common.platform_type.macos or
(
platform_type == apple_common.platform_type.watchos and
not _is_xcode_at_least_version(xcode_config, "12.5")
)
):
return None

# All platforms except macOS and watchOS have a
# `Developer/Library/Frameworks` directory in their SDK root.
return paths.join(apple_toolchain.sdk_dir(), "Developer/Library/Frameworks")

def _default_linker_opts(
Expand Down Expand Up @@ -189,76 +203,37 @@ def _default_linker_opts(
platform_developer_framework_dir = _platform_developer_framework_dir(
apple_toolchain,
apple_fragment,
xcode_config,
)
sdk_developer_framework_dir = _sdk_developer_framework_dir(
apple_toolchain,
apple_fragment,
xcode_config,
)
linkopts = []

uses_runtime_in_os = _is_xcode_at_least_version(xcode_config, "10.2")
if uses_runtime_in_os:
# Starting with Xcode 10.2, Apple forbids statically linking to the
# Swift runtime. The libraries are distributed with the OS and located
# in /usr/lib/swift.
swift_subdir = "swift"
linkopts.append("-Wl,-rpath,/usr/lib/swift")
elif is_static:
# This branch and the branch below now only support Xcode 10.1 and
# below. Eventually, once we drop support for those versions, they can
# be deleted.
swift_subdir = "swift_static"
linkopts.extend([
"-Wl,-force_load_swift_libs",
"-framework",
"Foundation",
"-lstdc++",
])
else:
swift_subdir = "swift"

swift_lib_dir = (
"{developer_dir}/Toolchains/{toolchain}.xctoolchain/" +
"usr/lib/{swift_subdir}/{platform}"
).format(
developer_dir = apple_toolchain.developer_dir(),
platform = platform.name_in_plist.lower(),
swift_subdir = swift_subdir,
toolchain = "XcodeDefault",
)

# TODO(b/128303533): It's possible to run Xcode 10.2 on a version of macOS
# 10.14.x that does not yet include `/usr/lib/swift`. Later Xcode 10.2 betas
# have deleted the `swift_static` directory, so we must manually add the
# dylibs to the binary's rpath or those binaries won't be able to run at
# all. This is added after `/usr/lib/swift` above so the system versions
# will always be preferred if they are present. This workaround can be
# removed once Xcode 10.2 and macOS 10.14.4 are out of beta.
if uses_runtime_in_os and platform == apple_common.platform.macos:
linkopts.append("-Wl,-rpath,{}".format(swift_lib_dir))

linkopts.extend(
[
"-F{}".format(path)
for path in compact([
platform_developer_framework_dir,
sdk_developer_framework_dir,
])
] + [
"-L{}".format(swift_lib_dir),
# TODO(b/112000244): These should get added by the C++ Starlark API,
# but we're using the "c++-link-executable" action right now instead
# of "objc-executable" because the latter requires additional
# variables not provided by cc_common. Figure out how to handle this
# correctly.
"-ObjC",
"-Wl,-objc_abi_version,2",
],
swift_lib_dir = paths.join(
apple_toolchain.developer_dir(),
"Toolchains/XcodeDefault.xctoolchain/usr/lib/swift",
platform.name_in_plist.lower(),
)

use_system_swift_libs = _is_xcode_at_least_version(xcode_config, "11.0")
if use_system_swift_libs:
linkopts.append("-L/usr/lib/swift")
linkopts = [
"-F{}".format(path)
for path in compact([
platform_developer_framework_dir,
sdk_developer_framework_dir,
])
] + [
"-Wl,-rpath,/usr/lib/swift",
"-L{}".format(swift_lib_dir),
"-L/usr/lib/swift",
# TODO(b/112000244): These should get added by the C++ Starlark API,
# but we're using the "c++-link-executable" action right now instead
# of "objc-executable" because the latter requires additional
# variables not provided by cc_common. Figure out how to handle this
# correctly.
"-ObjC",
"-Wl,-objc_abi_version,2",
]

# Frameworks in the platform's developer frameworks directory (like XCTest,
# but also StoreKitTest on macOS) contain the actual binary for that
Expand Down Expand Up @@ -331,7 +306,8 @@ def _all_action_configs(
apple_toolchain,
generated_header_rewriter,
needs_resource_directory,
target_triple):
target_triple,
xcode_config):
"""Returns the action configurations for the Swift toolchain.
Args:
Expand All @@ -348,17 +324,20 @@ def _all_action_configs(
needs_resource_directory: If True, the toolchain needs the resource
directory passed explicitly to the compiler.
target_triple: The target triple.
xcode_config: The Xcode configuration.
Returns:
The action configurations for the Swift toolchain.
"""
platform_developer_framework_dir = _platform_developer_framework_dir(
apple_toolchain,
apple_fragment,
xcode_config,
)
sdk_developer_framework_dir = _sdk_developer_framework_dir(
apple_toolchain,
apple_fragment,
xcode_config,
)
developer_framework_dirs = compact([
platform_developer_framework_dir,
Expand Down Expand Up @@ -472,7 +451,6 @@ def _all_tool_configs(
generated_header_rewriter,
swift_executable,
toolchain_root,
use_param_file,
xcode_config):
"""Returns the tool configurations for the Swift toolchain.
Expand All @@ -487,8 +465,6 @@ def _all_tool_configs(
swift_executable: A custom Swift driver executable to be used during the
build, if provided.
toolchain_root: The root directory of the toolchain, if provided.
use_param_file: If True, actions should have their arguments written to
param files.
xcode_config: The `apple_common.XcodeVersionConfig` provider.
Returns:
Expand All @@ -514,7 +490,7 @@ def _all_tool_configs(
execution_requirements = execution_requirements,
swift_executable = swift_executable,
toolchain_root = toolchain_root,
use_param_file = use_param_file,
use_param_file = True,
worker_mode = "persistent",
),
}
Expand All @@ -528,25 +504,13 @@ def _all_tool_configs(
execution_requirements = execution_requirements,
swift_executable = swift_executable,
toolchain_root = toolchain_root,
use_param_file = use_param_file,
use_param_file = True,
worker_mode = "wrap",
)
)

return tool_configs

def _is_macos(platform):
"""Returns `True` if the given platform is macOS.
Args:
platform: An `apple_platform` value describing the platform for which a
target is being built.
Returns:
`True` if the given platform is macOS.
"""
return platform.platform_type == apple_common.platform_type.macos

def _is_xcode_at_least_version(xcode_config, desired_version):
"""Returns True if we are building with at least the given Xcode version.
Expand Down Expand Up @@ -661,31 +625,20 @@ def _xcode_swift_toolchain_impl(ctx):
cpp_fragment = ctx.fragments.cpp,
) + features_from_swiftcopts(swiftcopts = ctx.fragments.swift.copts())
requested_features.extend(ctx.features)
requested_features.append(SWIFT_FEATURE_BUNDLED_XCTESTS)
requested_features.extend(
_features_for_bitcode_mode(apple_fragment.bitcode_mode),
)

# TODO(b/142867898): Added to match existing Bazel Objective-C module map
# behavior; remove it when possible.
requested_features.append(SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS)

# Xcode 10.0 implies Swift 4.2.
if _is_xcode_at_least_version(xcode_config, "10.0"):
use_param_file = True
requested_features.append(SWIFT_FEATURE_ENABLE_BATCH_MODE)
requested_features.append(SWIFT_FEATURE_USE_RESPONSE_FILES)
else:
use_param_file = False

# Xcode 10.2 implies Swift 5.0.
if _is_xcode_at_least_version(xcode_config, "10.2"):
requested_features.append(SWIFT_FEATURE_DEBUG_PREFIX_MAP)

# Xcode 11.0 implies Swift 5.1.
if _is_xcode_at_least_version(xcode_config, "11.0"):
requested_features.append(SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION)
requested_features.append(SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS)
requested_features.extend([
SWIFT_FEATURE_BUNDLED_XCTESTS,
SWIFT_FEATURE_ENABLE_BATCH_MODE,
SWIFT_FEATURE_USE_RESPONSE_FILES,
SWIFT_FEATURE_DEBUG_PREFIX_MAP,
SWIFT_FEATURE_SUPPORTS_LIBRARY_EVOLUTION,
SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS,
# TODO(b/142867898): Added to match existing Bazel Objective-C module
# map behavior; remove it when possible.
SWIFT_FEATURE_MODULE_MAP_NO_PRIVATE_HEADERS,
])

# Xcode 12.5 implies Swift 5.4.
if _is_xcode_at_least_version(xcode_config, "12.5"):
Expand All @@ -702,7 +655,6 @@ def _xcode_swift_toolchain_impl(ctx):
generated_header_rewriter = generated_header_rewriter,
swift_executable = swift_executable,
toolchain_root = toolchain_root,
use_param_file = use_param_file,
xcode_config = xcode_config,
)
all_action_configs = _all_action_configs(
Expand All @@ -716,6 +668,7 @@ def _xcode_swift_toolchain_impl(ctx):
generated_header_rewriter = generated_header_rewriter,
needs_resource_directory = swift_executable or toolchain_root,
target_triple = target,
xcode_config = xcode_config,
)

# Xcode toolchains don't pass any files explicitly here because they're
Expand Down

0 comments on commit bf5fccc

Please sign in to comment.