Skip to content

Commit

Permalink
mobile: add flag to build Swift with `-enable-experimental-cxx-intero…
Browse files Browse the repository at this point in the history
…p` (envoyproxy#25971)

The Swift project has had experimental C++ interoperability support for
a few years now. For example, a
[workgroup](https://forums.swift.org/t/swift-and-c-interoperability-workgroup-announcement/54998/1)
was started in early 2022. C++ interop is considered experimental
because it requires passing an additional flag to the Swift compiler
(`-enable-experimental-cxx-interop`) and the feature has no source or
ABI stability guarantees.

Envoy Mobile in particular would benefit greatly from Swift / C++
interop because it would allow us to shed the intermediate Objective-C
layer that only exists to bridge Envoy's C++ APIs with the consumer-
facing Swift API.

This change proposes a build flag to optionally build the Swift parts of
Envoy Mobile with C++ interop enabled, and enables it by default.

Future changes will gradually add Swift code that interacts directly
with C++ APIs.

Please read the [C++ Interoperability Status](https://github.com/apple/swift/blob/main/docs/CppInteroperability/CppInteroperabilityStatus.md)
to learn more about the current state of things.

Signed-off-by: JP Simard <[email protected]>
  • Loading branch information
jpsim authored Mar 9, 2023
1 parent b488e33 commit abeff27
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 8 deletions.
1 change: 1 addition & 0 deletions .github/workflows/compile_time_options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
--remote_header="Authorization=Bearer $GITHUB_TOKEN" \
--define=admin_html=enabled \
--define=envoy_mobile_request_compression=disabled \
--define=envoy_mobile_swift_cxx_interop=disabled \
--@envoy//bazel:http3=False \
--@com_envoyproxy_protoc_gen_validate//bazel:template-flavor= \
//library/swift:ios_framework
Expand Down
1 change: 1 addition & 0 deletions mobile/.bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ build --define=admin_html=disabled
build --define=static_extension_registration=disabled
build --define=admin_functionality=disabled
build --define=library_autolink=disabled
build --define=envoy_mobile_swift_cxx_interop=enabled
build --experimental_inmemory_dotd_files
build --experimental_inmemory_jdeps_files
build --features=debug_prefix_map_pwd_is_dot
Expand Down
5 changes: 5 additions & 0 deletions mobile/bazel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ licenses(["notice"]) # Apache 2

envoy_package()

config_setting(
name = "envoy_mobile_swift_cxx_interop",
values = {"define": "envoy_mobile_swift_cxx_interop=enabled"},
)

kt_jvm_library(
name = "envoy_mobile_test_suite",
srcs = [
Expand Down
8 changes: 8 additions & 0 deletions mobile/library/swift/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ swift_library(
"stats/Tags.swift",
"stats/TagsBuilder.swift",
] + ["@envoy_mobile_extra_swift_sources//:extra_swift_srcs"],
copts = select({
"//bazel:envoy_mobile_swift_cxx_interop": [
"-enable-experimental-cxx-interop",
"-Xcc",
"-std=c++17",
],
"//conditions:default": [],
}),
defines = envoy_mobile_defines("@envoy"),
features = [
"swift.emit_symbol_graph",
Expand Down
7 changes: 3 additions & 4 deletions mobile/library/swift/HeadersBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import Foundation
private let kRestrictedPrefixes = [":", "x-envoy-mobile"]

private func isRestrictedHeader(name: String) -> Bool {
let isHostHeader = name.caseInsensitiveCompare("host") == .orderedSame
lazy var hasRestrictedPrefix = kRestrictedPrefixes
.contains { name.range(of: $0, options: [.caseInsensitive, .anchored]) != nil }
return isHostHeader || hasRestrictedPrefix
let lowercasedName = name.lowercased()
let isHostHeader = lowercasedName == "host"
return isHostHeader || kRestrictedPrefixes.contains(where: lowercasedName.starts(with:))
}

/// Base builder class used to construct `Headers` instances.
Expand Down
2 changes: 1 addition & 1 deletion mobile/library/swift/Stream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class Stream: NSObject {
/// callback.
///
/// - returns: This stream, for chaining syntax.
public func readData(_ byteCount: size_t) -> Stream {
public func readData(_ byteCount: Int) -> Stream {
self.underlyingStream.readData(byteCount)
return self
}
Expand Down
23 changes: 20 additions & 3 deletions mobile/library/swift/stats/Element.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,30 @@ public final class Element: NSObject, ExpressibleByStringLiteral {
internal let value: String

public init(stringLiteral value: String) {
guard value.range(of: kPattern, options: .regularExpression) != nil else {
preconditionFailure("Element values must conform to the regex '\(kPattern)'.")
}
precondition(
value.matchesStatsElementPattern,
"Element values must conform to the regex '\(kPattern)'."
)
self.value = value
}

public override func isEqual(_ object: Any?) -> Bool {
return (object as? Element)?.value == self.value
}
}

extension String {
var matchesStatsElementPattern: Bool {
if #available(iOS 16.0, macOS 13.0, *) {
return self.contains(/^[A-Za-z_]+$/)
}

// `std` is the name of the C++ stdlib when importing it into Swift.
// So effectively this checks if we're compiling with `-enable-experimental-cxx-interop`.
#if canImport(std)
return (self as NSString).range(of: kPattern, options: regularExpression).location != NSNotFound
#else
return self.range(of: kPattern, options: .regularExpression) != nil
#endif
}
}

0 comments on commit abeff27

Please sign in to comment.