Skip to content
Merged
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
13 changes: 13 additions & 0 deletions swift/internal/api.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,19 @@ directly reference any other symbols in the object file that adds that
conformance.
""",
),
"generated_header_name": attr.string(
doc = """\
The name of the generated Objective-C interface header. This name must end with
a `.h` extension and cannot contain any path separators.

If this attribute is not specified, then the default behavior is to name the
header `${target_name}-Swift.h`.

This attribute is ignored if the toolchain does not support generating headers
or if the target has the `swift.no_generated_header` feature enabled.
""",
mandatory = False,
),
},
)

Expand Down
93 changes: 67 additions & 26 deletions swift/internal/compiling.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ def compile(
copts = [],
defines = [],
deps = [],
generated_header_name = None,
genfiles_dir = None):
"""Compiles a Swift module.

Expand Down Expand Up @@ -759,6 +760,9 @@ def compile(
deps: Dependencies of the target being compiled. These targets must
propagate one of the following providers: `CcInfo`, `SwiftInfo`, or
`apple_common.Objc`.
generated_header_name: The name of the Objective-C generated header that
should be generated for this module. If omitted, the name
`${target_name}-Swift.h` will be used.
genfiles_dir: The Bazel `*-genfiles` directory root. If provided, its
path is added to ClangImporter's header search paths for
compatibility with Bazel's C++ and Objective-C rules which support
Expand Down Expand Up @@ -800,6 +804,7 @@ def compile(
"""
compile_outputs, other_outputs = _declare_compile_outputs(
actions = actions,
generated_header_name = generated_header_name,
feature_configuration = feature_configuration,
module_name = module_name,
srcs = srcs,
Expand Down Expand Up @@ -912,6 +917,7 @@ def get_implicit_deps(feature_configuration, swift_toolchain):

def _declare_compile_outputs(
actions,
generated_header_name,
feature_configuration,
module_name,
srcs,
Expand All @@ -921,6 +927,8 @@ def _declare_compile_outputs(

Args:
actions: The object used to register actions.
generated_header_name: The desired name of the generated header for this
module, or `None` to use `${target_name}-Swift.h`.
feature_configuration: A feature configuration obtained from
`swift_common.configure_features`.
module_name: The name of the Swift module being compiled.
Expand Down Expand Up @@ -978,43 +986,48 @@ def _declare_compile_outputs(
else:
stats_directory = None

# If supported, generate a Swift header for this library so that it can be
# If supported, generate the Swift header for this library so that it can be
# included by Objective-C code that depends on it.
if not is_feature_enabled(
feature_configuration = feature_configuration,
feature_name = SWIFT_FEATURE_NO_GENERATED_HEADER,
):
generated_header = derived_files.objc_header(
actions = actions,
target_name = target_name,
)

# Create a module map for the generated header file. This ensures that
# inclusions of it are treated modularly, not textually.
#
# Caveat: Generated module maps are incompatible with the hack that some
# folks are using to support mixed Objective-C and Swift modules. This
# trap door lets them escape the module redefinition error, with the
# caveat that certain import scenarios could lead to incorrect behavior
# because a header can be imported textually instead of modularly.
if not is_feature_enabled(
feature_configuration = feature_configuration,
feature_name = SWIFT_FEATURE_NO_GENERATED_MODULE_MAP,
):
generated_module_map = derived_files.module_map(
if generated_header_name:
generated_header = _declare_validated_generated_header(
actions = actions,
target_name = target_name,
generated_header_name = generated_header_name,
)
_write_objc_header_module_map(
else:
generated_header = derived_files.default_generated_header(
actions = actions,
module_name = module_name,
objc_header = generated_header,
output = generated_module_map,
target_name = target_name,
)
else:
generated_module_map = None
else:
generated_header = None

# If not disabled, create a module map for the generated header file. This
# ensures that inclusions of it are treated modularly, not textually.
#
# Caveat: Generated module maps are incompatible with the hack that some
# folks are using to support mixed Objective-C and Swift modules. This
# trap door lets them escape the module redefinition error, with the
# caveat that certain import scenarios could lead to incorrect behavior
# because a header can be imported textually instead of modularly.
if generated_header and not is_feature_enabled(
feature_configuration = feature_configuration,
feature_name = SWIFT_FEATURE_NO_GENERATED_MODULE_MAP,
):
generated_module_map = derived_files.module_map(
actions = actions,
target_name = target_name,
)
_write_objc_header_module_map(
actions = actions,
module_name = module_name,
objc_header = generated_header,
output = generated_module_map,
)
else:
generated_module_map = None

# Now, declare outputs like object files for which there may be one or many,
Expand Down Expand Up @@ -1164,6 +1177,34 @@ def _declare_multiple_outputs_and_write_output_file_map(
output_file_map = output_map_file,
)

def _declare_validated_generated_header(actions, generated_header_name):
"""Validates and declares the explicitly named generated header.

If the file does not have a `.h` extension or conatins path separators, the
build will fail.

Args:
actions: The context's `actions` object.
generated_header_name: The desired name of the generated header.

Returns:
A `File` that should be used as the output for the generated header.
"""
if "/" in generated_header_name:
fail(
"The generated header for a Swift module may not contain " +
"directory components (got '{}').".format(generated_header_name),
)

extension = paths.split_extension(generated_header_name)[1]
if extension != ".h":
fail(
"The generated header for a Swift module must have a '.h' " +
"extension (got '{}').".format(generated_header_name),
)

return actions.declare_file(generated_header_name)

def _merge_targets_providers(supports_objc_interop, targets):
"""Merges the compilation-related providers for the given targets.

Expand Down
26 changes: 13 additions & 13 deletions swift/internal/derived_files.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ def _autolink_flags(actions, target_name):
"""
return actions.declare_file("{}.autolink".format(target_name))

def _default_generated_header(actions, target_name):
"""Declares the automatically-named generated header for a Swift target.

Args:
actions: The context's actions object.
target_name: The name of the target being built.

Returns:
The declared `File`.
"""
return actions.declare_file("{}-Swift.h".format(target_name))

def _executable(actions, target_name):
"""Declares a file for the executable created by a binary or test rule.

Expand Down Expand Up @@ -122,18 +134,6 @@ def _modulewrap_object(actions, target_name):
"""
return actions.declare_file("{}.modulewrap.o".format(target_name))

def _objc_header(actions, target_name):
"""Declares the generated header file exposing Swift APIs to Objective-C.

Args:
actions: The context's actions object.
target_name: The name of the target being built.

Returns:
The declared `File`.
"""
return actions.declare_file("{}-Swift.h".format(target_name))

def _partial_swiftmodule(actions, target_name, src):
"""Declares a file for a partial Swift module created during compilation.

Expand Down Expand Up @@ -291,12 +291,12 @@ def _xctest_runner_script(actions, target_name):

derived_files = struct(
autolink_flags = _autolink_flags,
default_generated_header = _default_generated_header,
executable = _executable,
indexstore_directory = _indexstore_directory,
intermediate_object_file = _intermediate_object_file,
module_map = _module_map,
modulewrap_object = _modulewrap_object,
objc_header = _objc_header,
partial_swiftmodule = _partial_swiftmodule,
reexport_modules_src = _reexport_modules_src,
static_archive = _static_archive,
Expand Down
1 change: 1 addition & 0 deletions swift/internal/swift_library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def _swift_library_impl(ctx):
defines = ctx.attr.defines,
deps = deps + private_deps,
feature_configuration = feature_configuration,
generated_header_name = ctx.attr.generated_header_name,
genfiles_dir = ctx.genfiles_dir,
module_name = module_name,
srcs = srcs,
Expand Down
3 changes: 3 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
load(":generated_header_tests.bzl", "generated_header_test_suite")
load(":private_deps_tests.bzl", "private_deps_test_suite")

licenses(["notice"])

generated_header_test_suite()

private_deps_test_suite()

test_suite(
Expand Down
35 changes: 35 additions & 0 deletions test/fixtures/generated_header/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
load("//swift:swift.bzl", "swift_library")
load("//test/fixtures:common.bzl", "FIXTURE_TAGS")

package(
default_visibility = ["//test:__subpackages__"],
)

licenses(["notice"])

swift_library(
name = "auto_header",
srcs = ["Empty.swift"],
tags = FIXTURE_TAGS,
)

swift_library(
name = "explicit_header",
srcs = ["Empty.swift"],
generated_header_name = "SomeOtherName.h",
tags = FIXTURE_TAGS,
)

swift_library(
name = "invalid_extension",
srcs = ["Empty.swift"],
generated_header_name = "Invalid.extension",
tags = FIXTURE_TAGS,
)

swift_library(
name = "invalid_path_separator",
srcs = ["Empty.swift"],
generated_header_name = "Invalid/Separator.h",
tags = FIXTURE_TAGS,
)
1 change: 1 addition & 0 deletions test/fixtures/generated_header/Empty.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Intentionally empty.
Loading