Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions apple/internal/apple_xcframework_import.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,10 @@ def _get_xcframework_library(
headers: List of File referencing XCFramework library header files. This can be either
a single tree artifact or a list of regular artifacts.
clang_module_map: File referencing the XCFramework library Clang modulemap file.
swift_module_interface: File referencing the XCFramework library Swift module interface
file (`.swiftinterface`).
swift_module_interface: File referencing the XCFramework library's primary Swift
module interface file (`.swiftinterface`).
swift_module_interfaces: List of File referencing all XCFramework library Swift
module interface files required during compilation.
"""
xcframework_library = None
if not parse_xcframework_info_plist:
Expand Down Expand Up @@ -255,6 +257,7 @@ def _get_xcframework_library_from_paths(*, target_triplet, xcframework):
includes = includes,
clang_module_map = module_maps[0] if module_maps else None,
swiftmodule = swiftmodules,
swift_module_interfaces = swift_module_interfaces,
swift_module_interface = swift_module_interfaces[0] if swift_module_interfaces else None,
)

Expand Down Expand Up @@ -416,6 +419,7 @@ def _get_xcframework_library_with_xcframework_processor(
includes = includes,
clang_module_map = module_map_file,
swiftmodule = [],
swift_module_interfaces = [swiftinterface_file] if swiftinterface_file else [],
swift_module_interface = swiftinterface_file,
framework_files = [],
)
Expand Down Expand Up @@ -535,7 +539,7 @@ def _apple_dynamic_xcframework_import_impl(ctx):
kind = "dynamic",
label = label,
libraries = [] if ctx.attr.bundle_only else [xcframework_library.binary],
swiftinterface_imports = [xcframework_library.swift_module_interface] if xcframework_library.swift_module_interface else [],
swiftinterface_imports = xcframework_library.swift_module_interfaces,
swiftmodule_imports = xcframework_library.swiftmodule,
)
providers.append(cc_info)
Expand Down Expand Up @@ -674,7 +678,7 @@ def _apple_static_xcframework_import_impl(ctx):
libraries = [xcframework_library.binary],
framework_includes = xcframework_library.framework_includes,
linkopts = sdk_linkopts + linkopts,
swiftinterface_imports = [xcframework_library.swift_module_interface] if xcframework_library.swift_module_interface else [],
swiftinterface_imports = xcframework_library.swift_module_interfaces,
swiftmodule_imports = xcframework_library.swiftmodule,
# User-specified includes are relative to the platform directory inside the xcframework.
# For framework XCFrameworks, binary is inside .framework bundle, so go up one level.
Expand Down
32 changes: 19 additions & 13 deletions apple/internal/framework_import_support.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -392,20 +392,26 @@ def _get_swift_module_files_with_target_triplet(target_triplet, swift_module_fil
if target_triplet.environment != "device":
environment = "-" + target_triplet.environment

target_triplet_file = files.get_file_with_name(
files = module_files.to_list(),
name = "{architecture}-{vendor}-{os}{environment}".format(
architecture = target_triplet.architecture,
environment = environment,
os = target_triplet.os,
vendor = target_triplet.vendor,
),
module_files_list = module_files.to_list()
target_triplet_name = "{architecture}-{vendor}-{os}{environment}".format(
architecture = target_triplet.architecture,
environment = environment,
os = target_triplet.os,
vendor = target_triplet.vendor,
)
architecture_file = files.get_file_with_name(
files = module_files.to_list(),
name = target_triplet.architecture,
)
filtered_files.append(target_triplet_file or architecture_file)

for file_name in [
target_triplet_name,
target_triplet_name + ".private",
target_triplet.architecture,
target_triplet.architecture + ".private",
]:
matching_file = files.get_file_with_name(
files = module_files_list,
name = file_name,
)
if matching_file:
filtered_files.append(matching_file)

return filtered_files

Expand Down
18 changes: 18 additions & 0 deletions test/starlark_tests/apple_dynamic_xcframework_import_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ load(
"//apple/build_settings:build_settings.bzl",
"build_settings_labels",
)
load(
"//test/starlark_tests/rules:action_inputs_test.bzl",
"make_action_inputs_test_rule",
)
load(
"//test/starlark_tests/rules:analysis_failure_message_test.bzl",
"analysis_failure_message_test",
Expand Down Expand Up @@ -49,6 +53,10 @@ analysis_output_group_info_files_with_xcframework_processor_test = make_analysis
build_settings_labels.parse_xcframework_info_plist: True,
})

action_inputs_with_ios_x86_64_platform_test = make_action_inputs_test_rule({
"//command_line_option:platforms": str(Label("@build_bazel_apple_support//platforms:ios_x86_64")),
})

def apple_dynamic_xcframework_import_test_suite(name):
"""Test suite for apple_dynamic_xcframework_import.
Expand Down Expand Up @@ -130,6 +138,16 @@ def apple_dynamic_xcframework_import_test_suite(name):
],
tags = [name],
)
action_inputs_with_ios_x86_64_platform_test(
name = "{}_declares_private_swiftinterface_inputs".format(name),
target_under_test = "//test/starlark_tests/targets_under_test/ios:dynamic_swift_xcframework_with_private_swiftinterface_depending_swift_lib",
mnemonic = "SwiftCompile",
expected_inputs = [
"Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.swiftinterface",
"Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.private.swiftinterface",
],
tags = [name],
)
archive_contents_test(
name = "{}_contains_implementation_deps_imported_xcframework_framework_files".format(name),
build_type = "simulator",
Expand Down
132 changes: 132 additions & 0 deletions test/starlark_tests/rules/action_inputs_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Copyright 2026 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Rules for testing the contents of action input artifacts."""

load("@bazel_skylib//lib:collections.bzl", "collections")
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "unittest")

def _action_inputs_test_impl(ctx):
env = analysistest.begin(ctx)
target_under_test = analysistest.target_under_test(env)

actions = analysistest.target_actions(env)
mnemonic = ctx.attr.mnemonic
matching_actions = [
action
for action in actions
if action.mnemonic == mnemonic
]

if not matching_actions:
actual_mnemonics = collections.uniq(
[action.mnemonic for action in actions],
)
unittest.fail(
env,
("Target '{}' registered no actions with the mnemonic '{}' " +
"(it had {}).").format(
str(target_under_test.label),
mnemonic,
actual_mnemonics,
),
)
return analysistest.end(env)

action_inputs = []
for action in matching_actions:
action_inputs.append([
file.short_path
for file in action.inputs.to_list()
])

if ctx.attr.expected_inputs:
matched_expected_inputs = False
for inputs in action_inputs:
contains_all_expected_inputs = True
for expected_input in ctx.attr.expected_inputs:
found_expected_input = False
for actual_input in inputs:
if expected_input in actual_input:
found_expected_input = True
break
if not found_expected_input:
contains_all_expected_inputs = False
break

if contains_all_expected_inputs:
matched_expected_inputs = True
break

if not matched_expected_inputs:
unittest.fail(
env,
("Expected at least one '{}' action for target '{}' to contain all expected " +
"inputs {}. Actual inputs: {}").format(
mnemonic,
str(target_under_test.label),
ctx.attr.expected_inputs,
action_inputs,
),
)
return analysistest.end(env)

if ctx.attr.not_expected_inputs:
for inputs in action_inputs:
for not_expected_input in ctx.attr.not_expected_inputs:
for actual_input in inputs:
if not_expected_input in actual_input:
unittest.fail(
env,
("Expected '{}' action for target '{}' to not contain input '{}', " +
"but it did: {}").format(
mnemonic,
str(target_under_test.label),
not_expected_input,
inputs,
),
)
return analysistest.end(env)

return analysistest.end(env)

def make_action_inputs_test_rule(config_settings = {}):
"""Returns a new `action_inputs_test`-like rule with custom configs."""
return analysistest.make(
_action_inputs_test_impl,
attrs = {
"expected_inputs": attr.string_list(
doc = """\
A list of path substrings that must all be present in the inputs of at least
one action with the requested mnemonic.
""",
),
"mnemonic": attr.string(
mandatory = True,
doc = """\
The mnemonic of the action to inspect on the target under test. It is expected
that at least one action with this mnemonic exists.
""",
),
"not_expected_inputs": attr.string_list(
doc = """\
A list of path substrings that must not be present in the inputs of any action
with the requested mnemonic.
""",
),
},
config_settings = config_settings,
)

action_inputs_test = make_action_inputs_test_rule()
47 changes: 47 additions & 0 deletions test/starlark_tests/targets_under_test/apple/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,53 @@ generate_dynamic_xcframework(
tags = common.fixture_tags,
)

genrule(
name = "swift_3p_xcframework_with_generated_header_and_private_swiftinterface",
testonly = True,
srcs = ["//test/starlark_tests/targets_under_test/ios:ios_swift_dynamic_xcframework"],
outs = [
"Swift3PFmwkWithGenHeader.xcframework/Info.plist",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Info.plist",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Swift3PFmwkWithGenHeader",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Headers/Swift3PFmwkWithGenHeader.h",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.swiftdoc",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.swiftinterface",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.private.swiftinterface",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Modules/module.modulemap",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Swift3PFmwkWithGenHeader",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Headers/Swift3PFmwkWithGenHeader.h",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Info.plist",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.swiftdoc",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.swiftinterface",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.private.swiftinterface",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.swiftdoc",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.swiftinterface",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.private.swiftinterface",
"Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/module.modulemap",
],
cmd = """
set -eu

for src in $(SRCS); do
rel_path="$${src#*Swift3PFmwkWithGenHeader.xcframework/}"
output_path="$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/$$rel_path"
mkdir -p "$$(dirname "$$output_path")"
cp "$$src" "$$output_path"
done

cp \
"$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.swiftinterface" \
"$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/ios-arm64/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.private.swiftinterface"
cp \
"$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.swiftinterface" \
"$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/arm64.private.swiftinterface"
cp \
"$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.swiftinterface" \
"$(RULEDIR)/Swift3PFmwkWithGenHeader.xcframework/ios-arm64_x86_64-simulator/Swift3PFmwkWithGenHeader.framework/Modules/Swift3PFmwkWithGenHeader.swiftmodule/x86_64.private.swiftinterface"
""",
tags = common.fixture_tags,
)

# ---------------------------------------------------------------------------------------
# Targets for Apple Core ML library tests.

Expand Down
16 changes: 16 additions & 0 deletions test/starlark_tests/targets_under_test/ios/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,15 @@ apple_dynamic_xcframework_import(
xcframework_imports = [":ios_swift_dynamic_xcframework"],
)

apple_dynamic_xcframework_import(
name = "ios_imported_swift_dynamic_xcframework_with_private_swiftinterface",
features = ["-swift.layering_check"],
tags = common.fixture_tags,
xcframework_imports = [
"//test/starlark_tests/targets_under_test/apple:swift_3p_xcframework_with_generated_header_and_private_swiftinterface",
],
)

genrule(
name = "ios_swift_dynamic_xcframework",
srcs = ["//test/starlark_tests/targets_under_test/apple:ios_swift_3p_xcframework_with_generated_header"],
Expand All @@ -3012,6 +3021,13 @@ genrule(
tags = common.fixture_tags,
)

swift_library(
name = "dynamic_swift_xcframework_with_private_swiftinterface_depending_swift_lib",
srcs = [":swift_with_dynamic_swift_framework_src"],
tags = common.fixture_tags,
deps = [":ios_imported_swift_dynamic_xcframework_with_private_swiftinterface"],
)

# Swift app with imported Objective-C static framework.
ios_application(
name = "swift_app_with_imported_static_fmwk",
Expand Down