diff --git a/doc/rules.md b/doc/rules.md
index 08c42c885..12c894e87 100755
--- a/doc/rules.md
+++ b/doc/rules.md
@@ -41,7 +41,7 @@ On this page:
swift_binary(name, deps, srcs, data, additional_linker_inputs, copts, defines, env, linkopts,
- malloc, module_name, package_name, plugins, stamp, swiftc_inputs)
+ linkshared, malloc, module_name, package_name, plugins, stamp, swiftc_inputs)
Compiles and links Swift code into an executable binary.
@@ -58,6 +58,9 @@ please use one of the platform-specific application rules in
[rules_apple](https://github.com/bazelbuild/rules_apple) instead of
`swift_binary`.
+Setting `linkshared = True` links a shared library instead of an executable;
+see the `linkshared` attribute.
+
**ATTRIBUTES**
@@ -72,6 +75,7 @@ please use one of the platform-specific application rules in
| defines | A list of defines to add to the compilation command line.
Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** `name=value` pairs.
Each string is prepended with `-D` and added to the command line. Unlike `copts`, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to `copts`, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | `[]` |
| env | Specifies additional environment variables to set when the test is executed by `bazel run` or `bazel test`.
The values of these environment variables are subject to `$(location)` and "Make variable" substitution.
NOTE: The environment variables are not set when you run the target outside of Bazel (for example, by manually executing the binary in `bazel-bin/`). | Dictionary: String -> String | optional | `{}` |
| linkopts | Additional linker options that should be passed to `clang`. These strings are subject to `$(location ...)` expansion. | List of strings | optional | `[]` |
+| linkshared | If `True`, link the target as a shared library / loadable module instead of an executable, similar to `cc_binary`'s `linkshared`. The binary has no `main` entry point and the renamed-entry-point machinery is disabled.
This produces a dynamic library named `lib.so` (`.dylib` on Apple platforms) suitable for loading with `dlopen` / `System.loadLibrary` (e.g. an Android JNI library; export functions with `@_cdecl`). | Boolean | optional | `False` |
| malloc | Override the default dependency on `malloc`.
By default, Swift binaries are linked against `@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use libc's `malloc`. This label must refer to a `cc_library` rule. | Label | optional | `"@bazel_tools//tools/cpp:malloc"` |
| module_name | The name of the Swift module being built.
If left unspecified, the module name will be computed based on the target's build label, by stripping the leading `//` and replacing `/`, `:`, and other non-identifier characters with underscores. | String | optional | `""` |
| package_name | The semantic package of the Swift target being built. Targets with the same package_name can access APIs using the 'package' access control modifier in Swift 5.9+. | String | optional | `""` |
diff --git a/examples/xplatform/shared_library/BUILD b/examples/xplatform/shared_library/BUILD
new file mode 100644
index 000000000..91e071230
--- /dev/null
+++ b/examples/xplatform/shared_library/BUILD
@@ -0,0 +1,11 @@
+load("//swift:swift_binary.bzl", "swift_binary")
+
+licenses(["notice"])
+
+# `linkshared` links a native dynamic library instead of an executable: a
+# `.dylib` on Apple platforms or a `.so` on Linux.
+swift_binary(
+ name = "shared_library",
+ srcs = ["greeting.swift"],
+ linkshared = True,
+)
diff --git a/examples/xplatform/shared_library/greeting.swift b/examples/xplatform/shared_library/greeting.swift
new file mode 100644
index 000000000..41a9a8e71
--- /dev/null
+++ b/examples/xplatform/shared_library/greeting.swift
@@ -0,0 +1,18 @@
+// Copyright 2024 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.
+
+@_cdecl("greeting")
+public func greeting() -> Int32 {
+ return 42
+}
diff --git a/swift/internal/feature_names.bzl b/swift/internal/feature_names.bzl
index e371f611a..c5b8928ae 100644
--- a/swift/internal/feature_names.bzl
+++ b/swift/internal/feature_names.bzl
@@ -321,6 +321,13 @@ SWIFT_FEATURE_DECLARE_SWIFTSOURCEINFO = "swift.emit_swiftsourceinfo"
# system command line limit.
SWIFT_FEATURE_NO_EMBED_DEBUG_MODULE = "swift.no_embed_debug_module"
+# If enabled, the entry point of a `swift_binary` is not renamed to a
+# target-specific symbol (which is otherwise aliased back to `main` at link
+# time so that the binary's code can also be linked into another binary, such
+# as a test executable). Toolchains whose linkers cannot create such aliases
+# (e.g. wasm-ld, which has no `--defsym`) should enable this feature.
+SWIFT_FEATURE_NO_ENTRY_POINT_RENAME = "swift.no_entry_point_rename"
+
# If enabled, the toolchain will directly generate from the raw proto files
# and not from the DescriptorSets.
#
diff --git a/swift/swift_binary.bzl b/swift/swift_binary.bzl
index 10689482c..2186bc9e4 100644
--- a/swift/swift_binary.bzl
+++ b/swift/swift_binary.bzl
@@ -14,6 +14,7 @@
"""Implementation of the `swift_binary` rule."""
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
@@ -22,6 +23,7 @@ load("//swift/internal:compiling.bzl", "compile")
load(
"//swift/internal:feature_names.bzl",
"SWIFT_FEATURE_ADD_TARGET_NAME_TO_OUTPUT",
+ "SWIFT_FEATURE_NO_ENTRY_POINT_RENAME",
)
load("//swift/internal:features.bzl", "is_feature_enabled")
load(
@@ -85,6 +87,15 @@ def _swift_binary_impl(ctx):
unsupported_features = ctx.disabled_features,
)
+ # A binary linked as a shared object (`linkshared`) has no `main`, so the
+ # entry-point rename (and the matching `--defsym main=...` at link time)
+ # must be skipped, just as it is when the toolchain requests it via
+ # `swift.no_entry_point_rename`.
+ skip_entry_point = ctx.attr.linkshared or is_feature_enabled(
+ feature_configuration = feature_configuration,
+ feature_name = SWIFT_FEATURE_NO_ENTRY_POINT_RENAME,
+ )
+
srcs = ctx.files.srcs
output_groups = {}
module_contexts = []
@@ -99,7 +110,21 @@ def _swift_binary_impl(ctx):
ctx.label,
feature_configuration = feature_configuration,
)
- entry_point_name = entry_point_function_name(module_name)
+
+ if skip_entry_point:
+ entry_point_name = None
+ entry_point_copts = []
+ else:
+ # Use a custom entry point name so that the binary's code can
+ # also be linked into another process (like a test executable)
+ # without having its main function collide.
+ entry_point_name = entry_point_function_name(module_name)
+ entry_point_copts = [
+ "-Xfrontend",
+ "-entry-point-function-name",
+ "-Xfrontend",
+ entry_point_name,
+ ]
include_dev_srch_paths = include_developer_search_paths(ctx.attr)
@@ -111,15 +136,7 @@ def _swift_binary_impl(ctx):
ctx,
ctx.attr.copts,
ctx.attr.swiftc_inputs,
- ) + _maybe_parse_as_library_copts(srcs) + [
- # Use a custom entry point name so that the binary's code can
- # also be linked into another process (like a test executable)
- # without having its main function collide.
- "-Xfrontend",
- "-entry-point-function-name",
- "-Xfrontend",
- entry_point_name,
- ],
+ ) + _maybe_parse_as_library_copts(srcs) + entry_point_copts,
defines = ctx.attr.defines,
feature_configuration = feature_configuration,
include_dev_srch_paths = include_dev_srch_paths,
@@ -178,6 +195,13 @@ def _swift_binary_impl(ctx):
else:
name = ctx.label.name
+ # `linkshared` produces a real dynamic library (`lib.so` / `.dylib`),
+ # matching `cc_binary`'s `linkshared`; otherwise an executable.
+ if ctx.attr.linkshared:
+ output_type = "dynamic_library"
+ else:
+ output_type = "executable"
+
linking_outputs = register_link_binary_action(
actions = ctx.actions,
additional_inputs = ctx.files.additional_linker_inputs,
@@ -189,18 +213,27 @@ def _swift_binary_impl(ctx):
label = ctx.label,
module_contexts = module_contexts,
name = name,
- output_type = "executable",
+ output_type = output_type,
stamp = ctx.attr.stamp,
toolchains = toolchains,
user_link_flags = binary_link_flags + entry_point_linkopts,
variables_extension = variables_extension,
)
+ if output_type == "dynamic_library":
+ library_to_link = linking_outputs.library_to_link
+ output_file = (
+ library_to_link.resolved_symlink_dynamic_library or
+ library_to_link.dynamic_library
+ )
+ else:
+ output_file = linking_outputs.executable
+
providers = [
DefaultInfo(
- executable = linking_outputs.executable,
+ executable = output_file,
files = depset(
- [linking_outputs.executable] + additional_debug_outputs,
+ [output_file] + additional_debug_outputs,
),
runfiles = ctx.runfiles(
collect_data = True,
@@ -278,9 +311,25 @@ def _swift_binary_impl(ctx):
return providers
swift_binary = rule(
- attrs = binary_rule_attrs(
- additional_deps_providers = [[SwiftCompilerPluginInfo]],
- stamp_default = -1,
+ attrs = dicts.add(
+ binary_rule_attrs(
+ additional_deps_providers = [[SwiftCompilerPluginInfo]],
+ stamp_default = -1,
+ ),
+ {
+ "linkshared": attr.bool(
+ default = False,
+ doc = """\
+If `True`, link the target as a shared library / loadable module instead of an
+executable, similar to `cc_binary`'s `linkshared`. The binary has no `main`
+entry point and the renamed-entry-point machinery is disabled.
+
+This produces a dynamic library named `lib.so` (`.dylib` on Apple
+platforms) suitable for loading with `dlopen` / `System.loadLibrary` (e.g. an
+Android JNI library; export functions with `@_cdecl`).
+""",
+ ),
+ },
),
doc = """\
Compiles and links Swift code into an executable binary.
@@ -296,6 +345,9 @@ If you want to create a multi-architecture binary or a bundled application,
please use one of the platform-specific application rules in
[rules_apple](https://github.com/bazelbuild/rules_apple) instead of
`swift_binary`.
+
+Setting `linkshared = True` links a shared library instead of an executable;
+see the `linkshared` attribute.
""",
exec_groups = {
# The `plugins` attribute associates its `exec` transition with this