Skip to content

Commit bce52f7

Browse files
authored
Add special -parse-as-library handling for swift_binary (bazelbuild#780)
This matches the behavior implemented in swiftlang/swift-package-manager#3410 for single file modules. Fixes bazelbuild#913
1 parent a666936 commit bce52f7

File tree

12 files changed

+164
-55
lines changed

12 files changed

+164
-55
lines changed

examples/apple/swift_explicit_modules/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ swift_c_module(
1313

1414
swift_binary(
1515
name = "hello_world",
16-
srcs = ["helloworld.swift"],
16+
srcs = ["main.swift"],
1717
deps = [":shims"],
1818
)

examples/xplatform/grpc/client_main.swift

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,44 @@ import NIOPosix
2020
import examples_xplatform_grpc_echo_proto
2121
import examples_xplatform_grpc_echo_client_services_swift
2222

23-
// Setup an `EventLoopGroup` for the connection to run on.
24-
//
25-
// See: https://github.com/apple/swift-nio#eventloops-and-eventloopgroups
26-
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
27-
28-
// Make sure the group is shutdown when we're done with it.
29-
defer {
30-
try! group.syncShutdownGracefully()
31-
}
32-
33-
// Configure the channel, we're not using TLS so the connection is `insecure`.
34-
let channel = try GRPCChannelPool.with(
35-
target: .host("localhost", port: 9000),
36-
transportSecurity: .plaintext,
37-
eventLoopGroup: group
38-
)
39-
40-
// Initialize the client using the same address the server is started on.
41-
let client = RulesSwift_Examples_Grpc_EchoServiceClient(channel: channel)
42-
43-
// Construct a request to the echo service.
44-
var request = RulesSwift_Examples_Grpc_EchoRequest.with {
45-
$0.contents = "Hello, world!"
46-
let timestamp = Google_Protobuf_Timestamp(date: Date())
47-
request.extra = try! Google_Protobuf_Any(message: timestamp)
48-
}
49-
50-
let call = client.echo(request)
51-
52-
// Make the remote method call and print the response we receive.
53-
do {
54-
let response = try call.response.wait()
55-
print(response.contents)
56-
} catch {
57-
print("Echo failed: \(error)")
23+
@main
24+
struct ClientMain {
25+
static func main() throws {
26+
// Setup an `EventLoopGroup` for the connection to run on.
27+
//
28+
// See: https://github.com/apple/swift-nio#eventloops-and-eventloopgroups
29+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
30+
31+
// Make sure the group is shutdown when we're done with it.
32+
defer {
33+
try! group.syncShutdownGracefully()
34+
}
35+
36+
// Configure the channel, we're not using TLS so the connection is `insecure`.
37+
let channel = try GRPCChannelPool.with(
38+
target: .host("localhost", port: 9000),
39+
transportSecurity: .plaintext,
40+
eventLoopGroup: group
41+
)
42+
43+
// Initialize the client using the same address the server is started on.
44+
let client = RulesSwift_Examples_Grpc_EchoServiceClient(channel: channel)
45+
46+
// Construct a request to the echo service.
47+
let request = RulesSwift_Examples_Grpc_EchoRequest.with {
48+
$0.contents = "Hello, world!"
49+
let timestamp = Google_Protobuf_Timestamp(date: Date())
50+
$0.extra = try! Google_Protobuf_Any(message: timestamp)
51+
}
52+
53+
let call = client.echo(request)
54+
55+
// Make the remote method call and print the response we receive.
56+
do {
57+
let response = try call.response.wait()
58+
print(response.contents)
59+
} catch {
60+
print("Echo failed: \(error)")
61+
}
62+
}
5863
}

examples/xplatform/grpc/server_main.swift

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import NIOPosix
1919
import examples_xplatform_grpc_echo_proto
2020
import examples_xplatform_grpc_echo_server_services_swift
2121

22-
2322
/// Concrete implementation of the `EchoService` service definition.
2423
class EchoProvider: RulesSwift_Examples_Grpc_EchoServiceProvider {
2524
var interceptors: RulesSwift_Examples_Grpc_EchoServiceServerInterceptorFactoryProtocol?
@@ -38,23 +37,28 @@ class EchoProvider: RulesSwift_Examples_Grpc_EchoServiceProvider {
3837
}
3938
}
4039

41-
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
42-
defer {
43-
try! group.syncShutdownGracefully()
44-
}
40+
@main
41+
struct ServerMain {
42+
static func main() throws {
43+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
44+
defer {
45+
try! group.syncShutdownGracefully()
46+
}
4547

46-
// Initialize and start the service.
47-
let server = Server.insecure(group: group)
48-
.withServiceProviders([EchoProvider()])
49-
.bind(host: "0.0.0.0", port: 9000)
48+
// Initialize and start the service.
49+
let server = Server.insecure(group: group)
50+
.withServiceProviders([EchoProvider()])
51+
.bind(host: "0.0.0.0", port: 9000)
5052

51-
server.map {
52-
$0.channel.localAddress
53-
}.whenSuccess { address in
54-
print("server started on port \(address!.port!)")
55-
}
53+
server.map {
54+
$0.channel.localAddress
55+
}.whenSuccess { address in
56+
print("server started on port \(address!.port!)")
57+
}
5658

57-
// Wait on the server's `onClose` future to stop the program from exiting.
58-
_ = try server.flatMap {
59-
$0.onClose
60-
}.wait()
59+
// Wait on the server's `onClose` future to stop the program from exiting.
60+
_ = try server.flatMap {
61+
$0.onClose
62+
}.wait()
63+
}
64+
}

swift/internal/swift_binary_test.bzl

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ def _configure_features_for_binary(
140140
unsupported_features = unsupported_features,
141141
)
142142

143+
def _maybe_parse_as_library_copts(srcs):
144+
"""Returns a list of compiler flags depending on `main.swift`'s presence.
145+
146+
Now that the `@main` attribute exists and is becoming more common, in the
147+
case there is a single file not named `main.swift`, we assume that it has a
148+
`@main` annotation, in which case it needs to be parsed as a library, not
149+
as if it has top level code. In the case this is the wrong assumption,
150+
compilation or linking will fail.
151+
152+
Args:
153+
srcs: A list of source files to check for the presence of `main.swift`.
154+
155+
Returns:
156+
A list of compiler flags to add to `copts`
157+
"""
158+
use_parse_as_library = len(srcs) == 1 and \
159+
srcs[0].basename != "main.swift"
160+
return ["-parse-as-library"] if use_parse_as_library else []
161+
143162
def _swift_linking_rule_impl(
144163
ctx,
145164
binary_path,
@@ -184,7 +203,8 @@ def _swift_linking_rule_impl(
184203
if not module_name:
185204
module_name = swift_common.derive_module_name(ctx.label)
186205

187-
copts = expand_locations(ctx, ctx.attr.copts, ctx.attr.swiftc_inputs)
206+
copts = expand_locations(ctx, ctx.attr.copts, ctx.attr.swiftc_inputs) + \
207+
_maybe_parse_as_library_copts(srcs)
188208

189209
module_context, cc_compilation_outputs, other_compilation_outputs = swift_common.compile(
190210
actions = ctx.actions,

test/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ load(":debug_settings_tests.bzl", "debug_settings_test_suite")
55
load(":features_tests.bzl", "features_test_suite")
66
load(":generated_header_tests.bzl", "generated_header_test_suite")
77
load(":linking_tests.bzl", "linking_test_suite")
8+
load(":mainattr_tests.bzl", "mainattr_test_suite")
89
load(":module_cache_settings_tests.bzl", "module_cache_settings_test_suite")
910
load(":output_file_map_tests.bzl", "output_file_map_test_suite")
1011
load(":private_deps_tests.bzl", "private_deps_test_suite")
@@ -28,6 +29,8 @@ generated_header_test_suite(name = "generated_header")
2829

2930
linking_test_suite(name = "linking")
3031

32+
mainattr_test_suite(name = "mainattr")
33+
3134
module_cache_settings_test_suite(name = "module_cache_settings")
3235

3336
output_file_map_test_suite(name = "output_file_map")

test/fixtures/mainattr/BUILD

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
load("//swift:swift.bzl", "swift_binary")
2+
3+
package(
4+
default_visibility = ["//test:__subpackages__"],
5+
)
6+
7+
swift_binary(
8+
name = "main",
9+
srcs = ["main.swift"],
10+
)
11+
12+
swift_binary(
13+
name = "custommain",
14+
srcs = ["custommain.swift"],
15+
)
16+
17+
swift_binary(
18+
name = "multiplefiles",
19+
srcs = [
20+
"file1.swift",
21+
"file2.swift",
22+
],
23+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@main
2+
struct Foo {
3+
static func main() {
4+
print("Hello, world!")
5+
}
6+
}

test/fixtures/mainattr/file1.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@main
2+
struct Foo {
3+
static func main() {
4+
print(foo())
5+
}
6+
}

test/fixtures/mainattr/file2.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
func foo() -> String {
2+
return "foo"
3+
}

test/fixtures/mainattr/main.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print("hi")

test/mainattr_tests.bzl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Tests for validating @main related usage"""
2+
3+
load(
4+
"@build_bazel_rules_swift//test/rules:action_command_line_test.bzl",
5+
"make_action_command_line_test_rule",
6+
)
7+
8+
mainattr_test = make_action_command_line_test_rule()
9+
10+
def mainattr_test_suite(name):
11+
mainattr_test(
12+
name = "{}_single_main".format(name),
13+
not_expected_argv = ["-parse-as-library"],
14+
mnemonic = "SwiftCompile",
15+
tags = [name],
16+
target_under_test = "@build_bazel_rules_swift//test/fixtures/mainattr:main",
17+
)
18+
19+
mainattr_test(
20+
name = "{}_single_custom_main".format(name),
21+
expected_argv = ["-parse-as-library"],
22+
mnemonic = "SwiftCompile",
23+
tags = [name],
24+
target_under_test = "@build_bazel_rules_swift//test/fixtures/mainattr:custommain",
25+
)
26+
27+
mainattr_test(
28+
name = "{}_multiple_files".format(name),
29+
not_expected_argv = ["-parse-as-library"],
30+
mnemonic = "SwiftCompile",
31+
tags = [name],
32+
target_under_test = "@build_bazel_rules_swift//test/fixtures/mainattr:multiplefiles",
33+
)
34+
35+
native.test_suite(
36+
name = name,
37+
tags = [name],
38+
)

0 commit comments

Comments
 (0)