From c0408858cdd65c6725adee0250b31d99551fe315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=20Do=C3=A3n?= Date: Sat, 5 Feb 2022 07:55:09 +0900 Subject: [PATCH] Add support for including headers when importing XCFrameworks assembled with static libraries (#1353) The paths are interpreted relative to the single platform directory inside the XCFramework for the platform being built. --- apple/internal/apple_framework_import.bzl | 53 +++++++++++++------ doc/rules-apple.md | 3 +- test/starlark_tests/resources/BUILD | 6 +-- .../targets_under_test/apple/BUILD | 1 + 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/apple/internal/apple_framework_import.bzl b/apple/internal/apple_framework_import.bzl index 6681857017..5b14c69657 100644 --- a/apple/internal/apple_framework_import.bzl +++ b/apple/internal/apple_framework_import.bzl @@ -203,13 +203,19 @@ def _objc_provider_with_dependencies(ctx, objc_provider_fields, additional_objc_ ] + additional_objc_infos return apple_common.new_objc_provider(**objc_provider_fields) -def _cc_info_with_dependencies(ctx, header_imports, additional_cc_infos = [], is_framework = True): +def _cc_info_with_dependencies( + ctx, + header_imports, + additional_cc_infos = [], + includes = [], + is_framework = True): """Returns a new CcInfo which includes transitive Cc dependencies.""" framework_search_paths = _framework_search_paths(header_imports) if is_framework else [] cc_info = CcInfo( compilation_context = cc_common.create_compilation_context( headers = depset(header_imports), framework_includes = depset(framework_search_paths), + includes = depset(includes), ), ) dep_cc_infos = [dep[CcInfo] for dep in ctx.attr.deps] @@ -388,19 +394,13 @@ def _get_framework_name(framework_imports): framework_dir = framework_groups.keys()[0] return paths.split_extension(paths.basename(framework_dir))[0] -def _get_xcframework_name(xcframework_imports): - """Returns the XCFramework name (the directory name without .xcframework).""" +def _process_xcframework_imports(ctx): + xcframework_groups = _grouped_xcframework_files(ctx.files.xcframework_imports) # We can just take the first key because the rule implementation guarantees # that we only have files for a single framework. - xcframework_groups = _grouped_xcframework_files(xcframework_imports) - xcframework_path = xcframework_groups.keys()[0] - return paths.split_extension(paths.basename(xcframework_path))[0] - -def _get_xcframework_imports(ctx): - xcframework_groups = _grouped_xcframework_files(ctx.files.xcframework_imports) - xcframework_name = _get_xcframework_name(ctx.files.xcframework_imports) xcframework_path = xcframework_groups.keys()[0] + xcframework_name = paths.split_extension(paths.basename(xcframework_path))[0] library_identifier = None if ctx.attr.library_identifiers: @@ -426,6 +426,8 @@ def _get_xcframework_imports(ctx): fail("Failed to figure out library identifiers. Please provide a " + "dictionary of library identifiers to `library_identifiers`.") + single_platform_dir = paths.join(xcframework_path, library_identifier) + # XCFramework with static frameworks platform_path = "{}/{}/{}.framework".format(xcframework_path, library_identifier, xcframework_name) framework_imports_for_platform = [f for f in ctx.files.xcframework_imports if platform_path in f.path] @@ -440,14 +442,14 @@ def _get_xcframework_imports(ctx): if not framework_imports_for_platform: fail("Couldn't find framework or library at path `{}`".format(platform_path)) - return framework_imports_for_platform + return xcframework_name, xcframework_path, single_platform_dir, framework_imports_for_platform def _common_dynamic_framework_import_impl(ctx, is_xcframework): """Common implementation for the apple_dynamic_framework_import and apple_dynamic_xcframework_import rules.""" providers = [] if is_xcframework: - framework_imports = _get_xcframework_imports(ctx) + _, _, _, framework_imports = _process_xcframework_imports(ctx) else: framework_imports = ctx.files.framework_imports @@ -516,8 +518,7 @@ def _common_static_framework_import_impl(ctx, is_xcframework): providers = [] if is_xcframework: - framework_imports = _get_xcframework_imports(ctx) - framework_name = _get_xcframework_name(framework_imports) + framework_name, xcframework_path, single_platform_dir, framework_imports = _process_xcframework_imports(ctx) else: framework_imports = ctx.files.framework_imports framework_name = _get_framework_name(framework_imports) @@ -616,8 +617,16 @@ def _common_static_framework_import_impl(ctx, is_xcframework): providers.append( _objc_provider_with_dependencies(ctx, objc_provider_fields, additional_objc_infos), ) + + includes = [] + if is_xcframework and not is_framework and ctx.attr.includes: + includes.extend([ + paths.join(single_platform_dir, x) + for x in ctx.attr.includes + ]) + providers.append( - _cc_info_with_dependencies(ctx, header_imports, additional_cc_infos, is_framework), + _cc_info_with_dependencies(ctx, header_imports, additional_cc_infos, includes, is_framework), ) # For now, Swift interop is restricted only to a Clang module map inside @@ -879,7 +888,6 @@ objc_library( """, ) -# TODO: Support for adding `includes` for XCFrameworks with static archives apple_static_xcframework_import = rule( implementation = _apple_static_xcframework_import_impl, fragments = ["apple"], @@ -887,6 +895,19 @@ apple_static_xcframework_import = rule( _xcframework_import_common_attrs, swift_common.toolchain_attrs(), { + "includes": attr.string_list( + doc = """ +List of `#include/#import` search paths to add to this target and all depending +targets. + +The paths are interpreted relative to the single platform directory inside the +XCFramework for the platform being built. + +These flags are added for this rule and every rule that depends on it. (Note: +not the rules it depends upon!) Be very careful, since this may have +far-reaching effects. +""", + ), "sdk_dylibs": attr.string_list( doc = """ Names of SDK .dylib libraries to link with. For instance, `libz` or diff --git a/doc/rules-apple.md b/doc/rules-apple.md index 21d091c09b..171a53d79e 100644 --- a/doc/rules-apple.md +++ b/doc/rules-apple.md @@ -137,7 +137,7 @@ objc_library( ## apple_static_xcframework_import
-apple_static_xcframework_import(name, alwayslink, deps, library_identifiers, sdk_dylibs,
+apple_static_xcframework_import(name, alwayslink, deps, includes, library_identifiers, sdk_dylibs,
                                 sdk_frameworks, weak_sdk_frameworks, xcframework_imports)
 
@@ -173,6 +173,7 @@ objc_library( | name | A unique name for this target. | Name | required | | | alwayslink | If true, any binary that depends (directly or indirectly) on this framework will link in all the object files for the framework file, even if some contain no symbols referenced by the binary. This is useful if your code isn't explicitly called by code in the binary; for example, if you rely on runtime checks for protocol conformances added in extensions in the library but do not directly reference any other symbols in the object file that adds that conformance. | Boolean | optional | False | | deps | A list of targets that are dependencies of the target being built, which will provide headers (if the importing XCFramework is a dynamic framework) and can be linked into that target. | List of labels | optional | [] | +| includes | List of #include/#import search paths to add to this target and all depending targets.

The paths are interpreted relative to the single platform directory inside the XCFramework for the platform being built.

These flags are added for this rule and every rule that depends on it. (Note: not the rules it depends upon!) Be very careful, since this may have far-reaching effects. | List of strings | optional | [] | | library_identifiers | An optional key-value map of platforms to the corresponding platform IDs (containing all supported architectures), relative to the XCFramework. The identifier keys should be case-insensitive variants of the values in [apple_common.platform](https://docs.bazel.build/versions/5.0.0/skylark/lib/apple_common.html#platform); for example, ios_device or ios_simulator. The identifier values should be case-sensitive variants of values that might be found in the LibraryIdentifier of an Info.plist file in the XCFramework's root; for example, ios-arm64_i386_x86_64-simulator or ios-arm64_armv7.

Passing this attribute should not be neccessary if the XCFramework follows the standard naming convention (that is, it was created by Xcode or Bazel). | Dictionary: String -> String | optional | {} | | sdk_dylibs | Names of SDK .dylib libraries to link with. For instance, libz or libarchive. libc++ is included automatically if the binary has any C++ or Objective-C++ sources in its dependency tree. When linking a binary, all libraries named in that binary's transitive dependency graph are used. | List of strings | optional | [] | | sdk_frameworks | Names of SDK frameworks to link with (e.g. AddressBook, QuartzCore). UIKit and Foundation are always included when building for the iOS, tvOS and watchOS platforms. For macOS, only Foundation is always included. When linking a top level binary, all SDK frameworks listed in that binary's transitive dependency graph are linked. | List of strings | optional | [] | diff --git a/test/starlark_tests/resources/BUILD b/test/starlark_tests/resources/BUILD index a4ead149a3..d5736780fa 100644 --- a/test/starlark_tests/resources/BUILD +++ b/test/starlark_tests/resources/BUILD @@ -670,11 +670,7 @@ swift_library( genrule( name = "objc_importing_imported_static_xcfw", outs = ["objc_importing_imported_static_xcfw.m"], - # Bazel doesn't do any xcframework processing like Xcode so this import has - # to be a full path. - cmd = """ -echo '#import "test/starlark_tests/targets_under_test/apple/ios_static_xcframework.xcframework/ios-arm64_x86_64-simulator/Headers/shared.h"' > $@ -""", + cmd = """echo '#import "shared.h"' > $@""", tags = FIXTURE_TAGS, ) diff --git a/test/starlark_tests/targets_under_test/apple/BUILD b/test/starlark_tests/targets_under_test/apple/BUILD index 6a344c7076..b4bdfd9756 100644 --- a/test/starlark_tests/targets_under_test/apple/BUILD +++ b/test/starlark_tests/targets_under_test/apple/BUILD @@ -455,6 +455,7 @@ apple_dynamic_xcframework_import( apple_static_xcframework_import( name = "ios_imported_static_xcframework", + includes = ["Headers"], tags = ["manual"], xcframework_imports = [":generated_ios_static_xcframework"], )