Skip to content

Commit

Permalink
Add special -parse-as-library handling for swift_binary (bazelbuild#780)
Browse files Browse the repository at this point in the history
This matches the behavior implemented in
swiftlang/swift-package-manager#3410 for single
file modules.

Fixes bazelbuild#913
  • Loading branch information
keith authored Oct 3, 2022
1 parent a666936 commit bce52f7
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 55 deletions.
2 changes: 1 addition & 1 deletion examples/apple/swift_explicit_modules/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ swift_c_module(

swift_binary(
name = "hello_world",
srcs = ["helloworld.swift"],
srcs = ["main.swift"],
deps = [":shims"],
)
75 changes: 40 additions & 35 deletions examples/xplatform/grpc/client_main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,44 @@ import NIOPosix
import examples_xplatform_grpc_echo_proto
import examples_xplatform_grpc_echo_client_services_swift

// Setup an `EventLoopGroup` for the connection to run on.
//
// See: https://github.com/apple/swift-nio#eventloops-and-eventloopgroups
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)

// Make sure the group is shutdown when we're done with it.
defer {
try! group.syncShutdownGracefully()
}

// Configure the channel, we're not using TLS so the connection is `insecure`.
let channel = try GRPCChannelPool.with(
target: .host("localhost", port: 9000),
transportSecurity: .plaintext,
eventLoopGroup: group
)

// Initialize the client using the same address the server is started on.
let client = RulesSwift_Examples_Grpc_EchoServiceClient(channel: channel)

// Construct a request to the echo service.
var request = RulesSwift_Examples_Grpc_EchoRequest.with {
$0.contents = "Hello, world!"
let timestamp = Google_Protobuf_Timestamp(date: Date())
request.extra = try! Google_Protobuf_Any(message: timestamp)
}

let call = client.echo(request)

// Make the remote method call and print the response we receive.
do {
let response = try call.response.wait()
print(response.contents)
} catch {
print("Echo failed: \(error)")
@main
struct ClientMain {
static func main() throws {
// Setup an `EventLoopGroup` for the connection to run on.
//
// See: https://github.com/apple/swift-nio#eventloops-and-eventloopgroups
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)

// Make sure the group is shutdown when we're done with it.
defer {
try! group.syncShutdownGracefully()
}

// Configure the channel, we're not using TLS so the connection is `insecure`.
let channel = try GRPCChannelPool.with(
target: .host("localhost", port: 9000),
transportSecurity: .plaintext,
eventLoopGroup: group
)

// Initialize the client using the same address the server is started on.
let client = RulesSwift_Examples_Grpc_EchoServiceClient(channel: channel)

// Construct a request to the echo service.
let request = RulesSwift_Examples_Grpc_EchoRequest.with {
$0.contents = "Hello, world!"
let timestamp = Google_Protobuf_Timestamp(date: Date())
$0.extra = try! Google_Protobuf_Any(message: timestamp)
}

let call = client.echo(request)

// Make the remote method call and print the response we receive.
do {
let response = try call.response.wait()
print(response.contents)
} catch {
print("Echo failed: \(error)")
}
}
}
40 changes: 22 additions & 18 deletions examples/xplatform/grpc/server_main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import NIOPosix
import examples_xplatform_grpc_echo_proto
import examples_xplatform_grpc_echo_server_services_swift


/// Concrete implementation of the `EchoService` service definition.
class EchoProvider: RulesSwift_Examples_Grpc_EchoServiceProvider {
var interceptors: RulesSwift_Examples_Grpc_EchoServiceServerInterceptorFactoryProtocol?
Expand All @@ -38,23 +37,28 @@ class EchoProvider: RulesSwift_Examples_Grpc_EchoServiceProvider {
}
}

let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}
@main
struct ServerMain {
static func main() throws {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer {
try! group.syncShutdownGracefully()
}

// Initialize and start the service.
let server = Server.insecure(group: group)
.withServiceProviders([EchoProvider()])
.bind(host: "0.0.0.0", port: 9000)
// Initialize and start the service.
let server = Server.insecure(group: group)
.withServiceProviders([EchoProvider()])
.bind(host: "0.0.0.0", port: 9000)

server.map {
$0.channel.localAddress
}.whenSuccess { address in
print("server started on port \(address!.port!)")
}
server.map {
$0.channel.localAddress
}.whenSuccess { address in
print("server started on port \(address!.port!)")
}

// Wait on the server's `onClose` future to stop the program from exiting.
_ = try server.flatMap {
$0.onClose
}.wait()
// Wait on the server's `onClose` future to stop the program from exiting.
_ = try server.flatMap {
$0.onClose
}.wait()
}
}
22 changes: 21 additions & 1 deletion swift/internal/swift_binary_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@ def _configure_features_for_binary(
unsupported_features = unsupported_features,
)

def _maybe_parse_as_library_copts(srcs):
"""Returns a list of compiler flags depending on `main.swift`'s presence.
Now that the `@main` attribute exists and is becoming more common, in the
case there is a single file not named `main.swift`, we assume that it has a
`@main` annotation, in which case it needs to be parsed as a library, not
as if it has top level code. In the case this is the wrong assumption,
compilation or linking will fail.
Args:
srcs: A list of source files to check for the presence of `main.swift`.
Returns:
A list of compiler flags to add to `copts`
"""
use_parse_as_library = len(srcs) == 1 and \
srcs[0].basename != "main.swift"
return ["-parse-as-library"] if use_parse_as_library else []

def _swift_linking_rule_impl(
ctx,
binary_path,
Expand Down Expand Up @@ -184,7 +203,8 @@ def _swift_linking_rule_impl(
if not module_name:
module_name = swift_common.derive_module_name(ctx.label)

copts = expand_locations(ctx, ctx.attr.copts, ctx.attr.swiftc_inputs)
copts = expand_locations(ctx, ctx.attr.copts, ctx.attr.swiftc_inputs) + \
_maybe_parse_as_library_copts(srcs)

module_context, cc_compilation_outputs, other_compilation_outputs = swift_common.compile(
actions = ctx.actions,
Expand Down
3 changes: 3 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ load(":debug_settings_tests.bzl", "debug_settings_test_suite")
load(":features_tests.bzl", "features_test_suite")
load(":generated_header_tests.bzl", "generated_header_test_suite")
load(":linking_tests.bzl", "linking_test_suite")
load(":mainattr_tests.bzl", "mainattr_test_suite")
load(":module_cache_settings_tests.bzl", "module_cache_settings_test_suite")
load(":output_file_map_tests.bzl", "output_file_map_test_suite")
load(":private_deps_tests.bzl", "private_deps_test_suite")
Expand All @@ -28,6 +29,8 @@ generated_header_test_suite(name = "generated_header")

linking_test_suite(name = "linking")

mainattr_test_suite(name = "mainattr")

module_cache_settings_test_suite(name = "module_cache_settings")

output_file_map_test_suite(name = "output_file_map")
Expand Down
23 changes: 23 additions & 0 deletions test/fixtures/mainattr/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
load("//swift:swift.bzl", "swift_binary")

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

swift_binary(
name = "main",
srcs = ["main.swift"],
)

swift_binary(
name = "custommain",
srcs = ["custommain.swift"],
)

swift_binary(
name = "multiplefiles",
srcs = [
"file1.swift",
"file2.swift",
],
)
6 changes: 6 additions & 0 deletions test/fixtures/mainattr/custommain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@main
struct Foo {
static func main() {
print("Hello, world!")
}
}
6 changes: 6 additions & 0 deletions test/fixtures/mainattr/file1.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@main
struct Foo {
static func main() {
print(foo())
}
}
3 changes: 3 additions & 0 deletions test/fixtures/mainattr/file2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
func foo() -> String {
return "foo"
}
1 change: 1 addition & 0 deletions test/fixtures/mainattr/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("hi")
38 changes: 38 additions & 0 deletions test/mainattr_tests.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Tests for validating @main related usage"""

load(
"@build_bazel_rules_swift//test/rules:action_command_line_test.bzl",
"make_action_command_line_test_rule",
)

mainattr_test = make_action_command_line_test_rule()

def mainattr_test_suite(name):
mainattr_test(
name = "{}_single_main".format(name),
not_expected_argv = ["-parse-as-library"],
mnemonic = "SwiftCompile",
tags = [name],
target_under_test = "@build_bazel_rules_swift//test/fixtures/mainattr:main",
)

mainattr_test(
name = "{}_single_custom_main".format(name),
expected_argv = ["-parse-as-library"],
mnemonic = "SwiftCompile",
tags = [name],
target_under_test = "@build_bazel_rules_swift//test/fixtures/mainattr:custommain",
)

mainattr_test(
name = "{}_multiple_files".format(name),
not_expected_argv = ["-parse-as-library"],
mnemonic = "SwiftCompile",
tags = [name],
target_under_test = "@build_bazel_rules_swift//test/fixtures/mainattr:multiplefiles",
)

native.test_suite(
name = name,
tags = [name],
)

0 comments on commit bce52f7

Please sign in to comment.