Skip to content

Commit 6309bef

Browse files
nglevinluispadron
authored andcommitted
Simplify the swift_stdlib_tool check for Swift runtime support by moving it to the main binary at analysis time.
Cherry-pick: 48a93e2
1 parent bc6e8d2 commit 6309bef

4 files changed

Lines changed: 87 additions & 70 deletions

File tree

apple/internal/partials/swift_dylibs.bzl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ _MIN_OS_PLATFORM_SWIFT_PRESENCE = {
7575
"watchos": apple_common.dotted_version("8.0"),
7676
}
7777

78+
# swift-stdlib-tool currently bundles an unnecessary copy of the Swift runtime
79+
# whenever it bundles the back-deploy version of the Swift concurrency
80+
# runtime. This is because the back-deploy version of the Swift concurrency
81+
# runtime contains an `@rpath`-relative reference to the Swift runtime due to
82+
# being built with a deployment target that predates the Swift runtime being
83+
# shipped with operating system.
84+
#
85+
# The Swift runtime only needs to be bundled if the binary's deployment target
86+
# is old enough that it may run on OS versions that lack the Swift runtime,
87+
# so we detect this scenario and remove the Swift runtime from the output
88+
# path.
89+
_MIN_OS_PLATFORM_SWIFT_RUNTIME_EMBEDDING = {
90+
"ios": apple_common.dotted_version("12.2"),
91+
"macos": apple_common.dotted_version("10.14.4"),
92+
"tvos": apple_common.dotted_version("12.2"),
93+
"visionos": apple_common.dotted_version("1.0"),
94+
"watchos": apple_common.dotted_version("5.2"),
95+
}
96+
7897
def _swift_dylib_action(
7998
*,
8099
actions,
@@ -100,6 +119,10 @@ def _swift_dylib_action(
100119
if platform_prerequisites.build_settings.disable_swift_stdlib_binary_thinning:
101120
swift_stdlib_tool_args.add("--disable_binary_thinning")
102121

122+
minimum_os = apple_common.dotted_version(platform_prerequisites.minimum_os)
123+
if minimum_os < _MIN_OS_PLATFORM_SWIFT_RUNTIME_EMBEDDING[platform_prerequisites.platform_type]:
124+
swift_stdlib_tool_args.append("--requires_bundled_swift_runtime")
125+
103126
apple_support.run(
104127
actions = actions,
105128
apple_fragment = platform_prerequisites.apple_fragment,

test/starlark_tests/ios_application_tests.bzl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,32 @@ def ios_application_test_suite(name):
188188
tags = [name],
189189
)
190190

191+
# Verify that Swift concurrency dylibs are packaged with the application, while the runtime is
192+
# not when the application is built for a target that supports the stable Swift ABI but lacks
193+
# OS support for the concurrency dylibs.
194+
archive_contents_test(
195+
name = "{}_device_swift_concurrency_dylibs_present".format(name),
196+
build_type = "device",
197+
target_under_test = "//test/starlark_tests/targets_under_test/ios:swift_app_requiring_concurrency_libs",
198+
contains = [
199+
"$BUNDLE_ROOT/Frameworks/libswift_Concurrency.dylib",
200+
"$ARCHIVE_ROOT/SwiftSupport/iphoneos/libswift_Concurrency.dylib",
201+
],
202+
not_contains = [
203+
"$BUNDLE_ROOT/Frameworks/libswiftCore.dylib",
204+
"$ARCHIVE_ROOT/SwiftSupport/iphoneos/libswiftCore.dylib",
205+
],
206+
tags = [name],
207+
)
208+
archive_contents_test(
209+
name = "{}_simulator_swift_concurrency_dylibs_present".format(name),
210+
build_type = "simulator",
211+
target_under_test = "//test/starlark_tests/targets_under_test/ios:swift_app_requiring_concurrency_libs",
212+
contains = ["$BUNDLE_ROOT/Frameworks/libswift_Concurrency.dylib"],
213+
not_contains = ["$BUNDLE_ROOT/Frameworks/libswiftCore.dylib"],
214+
tags = [name],
215+
)
216+
191217
apple_verification_test(
192218
name = "{}_imported_fmwk_codesign_test".format(name),
193219
build_type = "simulator",

test/starlark_tests/targets_under_test/ios/BUILD

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5660,3 +5660,21 @@ ios_extension(
56605660
"//test/starlark_tests/resources:objc_main_lib",
56615661
],
56625662
)
5663+
5664+
# ---------------------------------------------------------------------------------------
5665+
# Targets for Swift concurrency
5666+
5667+
ios_application(
5668+
name = "swift_app_requiring_concurrency_libs",
5669+
bundle_id = "com.google.example",
5670+
families = ["iphone"],
5671+
infoplists = [
5672+
"//test/starlark_tests/resources:Info.plist",
5673+
],
5674+
minimum_os_version = common.min_os_ios.concurrency_support,
5675+
provisioning_profile = "//test/testdata/provisioning:integration_testing_ios.mobileprovision",
5676+
tags = common.fixture_tags,
5677+
deps = [
5678+
"//test/starlark_tests/resources:swift_concurrency_main_lib",
5679+
],
5680+
)

tools/swift_stdlib_tool/swift_stdlib_tool.py

Lines changed: 20 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -25,73 +25,13 @@
2525
from tools.wrapper_common import execute
2626
from tools.wrapper_common import lipo
2727

28-
_OTOOL_MINIMUM_OS_VERSION_RE = re.compile(
29-
r"""
30-
(
31-
cmd\ LC_VERSION_MIN_(?P<lc_version_min_platform>[^\n]+)\n
32-
.*?
33-
version\ (?P<lc_version_min_version>\d+\.\d+)
34-
|
35-
cmd\ LC_BUILD_VERSION
36-
.*?
37-
platform\ (?P<lc_build_version_platform>[^\n]+)\n
38-
.*?
39-
minos\ (?P<lc_build_version_minos>\d+\.\d+)
40-
)
41-
""", re.VERBOSE | re.MULTILINE | re.DOTALL)
42-
43-
# Minimum OS versions after which the Swift runtime is packaged with the OS. If
44-
# the deployment target of a binary is greater than or equal to the versions
45-
# defined here it does not need to bundle the Swift runtime.
46-
_MIN_OS_PLATFORM_SWIFT_PRESENCE = {
47-
"ios": (12, 2),
48-
"iphoneos": (12, 2),
49-
"macos": (10, 14, 4),
50-
"macosx": (10, 14, 4),
51-
"tvos": (12, 2),
52-
"watchos": (5, 2),
53-
}
54-
55-
56-
def _deployment_target_requires_bundled_swift_runtime(platform, version):
57-
"""Returns true if the given deployment target requires a bundled copy of the Swift runtime."""
58-
59-
platform = platform.lower().replace("simulator", "")
60-
version = tuple(int(component) for component in version.split("."))
61-
62-
return version < _MIN_OS_PLATFORM_SWIFT_PRESENCE.get(platform, (0, 0))
63-
64-
65-
def _binary_requires_bundled_swift_runtime(binary):
66-
"""Returns true if the deployment target of the given binary requires a bundled copy of the Swift runtime."""
67-
68-
cmd = ["otool", "-lV", "-arch", "all", binary]
69-
_, stdout, stderr = execute.execute_and_filter_output(
70-
cmd, raise_on_failure=True)
71-
if stderr:
72-
print(stderr)
73-
74-
# Loop to ensure we process all architectures within the binary. Different
75-
# architectures may have different deployment targets.
76-
while True:
77-
match = _OTOOL_MINIMUM_OS_VERSION_RE.search(stdout)
78-
if not match:
79-
return False
80-
81-
groups = match.groupdict()
82-
# Only one of each alternative of platform and version can be set.
83-
platform = groups["lc_version_min_platform"] or groups[
84-
"lc_build_version_platform"]
85-
version = groups["lc_version_min_version"] or groups[
86-
"lc_build_version_minos"]
87-
88-
if _deployment_target_requires_bundled_swift_runtime(platform, version):
89-
return True
9028

91-
stdout = stdout[match.endpos:]
92-
93-
94-
def _copy_swift_stdlibs(binaries_to_scan, sdk_platform, destination_path):
29+
def _copy_swift_stdlibs(
30+
*,
31+
binaries_to_scan,
32+
sdk_platform,
33+
destination_path,
34+
requires_bundled_swift_runtime):
9535
"""Copies the Swift stdlibs required by the binaries to the destination."""
9636
# Rely on the swift-stdlib-tool to determine the subset of Swift stdlibs that
9737
# these binaries require.
@@ -127,9 +67,7 @@ def _copy_swift_stdlibs(binaries_to_scan, sdk_platform, destination_path):
12767
# is old enough that it may run on OS versions that lack the Swift runtime,
12868
# so we detect this scenario and remove the Swift runtime from the output
12969
# path.
130-
if not any(
131-
_binary_requires_bundled_swift_runtime(binary)
132-
for binary in binaries_to_scan):
70+
if not requires_bundled_swift_runtime:
13371
libswiftcore_path = os.path.join(destination_path, "libswiftCore.dylib")
13472
if os.path.exists(libswiftcore_path):
13573
os.remove(libswiftcore_path)
@@ -184,13 +122,25 @@ def main():
184122
"--output_path", type=str, required=True, help="path to save the Swift "
185123
"support libraries to"
186124
)
125+
parser.add_argument(
126+
"--requires_bundled_swift_runtime", action="store_true", default=False,
127+
help="""
128+
if true, indicates that the Swift runtime needs to be bundled with the
129+
binary
130+
"""
131+
)
187132
args = parser.parse_args()
188133

189134
# Create a temporary location for the unstripped Swift stdlibs.
190135
temp_path = tempfile.mkdtemp(prefix="swift_stdlib_tool.XXXXXX")
191136

192137
# Use the binaries to copy only the Swift stdlibs we need for this app.
193-
_copy_swift_stdlibs(args.binary, args.platform, temp_path)
138+
_copy_swift_stdlibs(
139+
binaries_to_scan=args.binary,
140+
sdk_platform=args.platform,
141+
destination_path=temp_path,
142+
requires_bundled_swift_runtime=args.requires_bundled_swift_runtime,
143+
)
194144

195145
# Determine the binary slices we need to strip with lipo.
196146
target_archs, _ = lipo.find_archs_for_binaries(args.binary)

0 commit comments

Comments
 (0)