Skip to content

Commit

Permalink
Add a CompileTest for "multi module" generation.
Browse files Browse the repository at this point in the history
- Add basic infrastructure to have Compile Tests.

- Add a specific MultiModule CompileTest.
  - This ensure that with `import public` is properly handle and the generated
    code compiles even when a needed type is coming from a public import.
  - So the any relevant code changes are more visible, check the the generated
    code so the diffs for an changes related to the test will be easily
    reviewed.
  - The SPM plugin doesn't support multi module generation
    (#1450), which is also why the
    code needs to be checked in, otherwise, it might simplify the tests.

Note: The whole concept of CompileTests doesn't have to be a set up as distinct
Swift Packages, but if we put the required targets in the main Package.swift,
all projects using swift-protobuf would end up having the "overhead" of those
test only targets, so I've errored on the side of a distinct package to make
sure there is no impact of these tests on the folks using swift-protobuf.
  • Loading branch information
thomasvl committed Jan 11, 2024
1 parent da0566a commit 2a383b6
Show file tree
Hide file tree
Showing 23 changed files with 1,229 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ jobs:
- name: Test SPM plugin
working-directory: main
run: make test-spm-plugin PROTOC=../protobuf/cmake_build/protoc
- name: Compilation Tests
working-directory: main
run: make compile-tests PROTOC=../protobuf/cmake_build/protoc

sanitizer_testing:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ xcbaselines
/docs
/build
mined_words.txt
/CompileTests/MultiModule/.build
/CompileTests/MultiModule/.swiftpm
/*DescriptorTestData.bin
/Package.resolved

Expand Down
40 changes: 40 additions & 0 deletions CompileTests/MultiModule/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// swift-tools-version: 5.6

import PackageDescription

let package = Package(
name: "CompileTests",
dependencies: [
.package(name: "swift-protobuf", path: "../..")
],
targets: [
.testTarget(
name: "Test1",
dependencies: ["ImportsAPublicly"]
),
.testTarget(
name: "Test2",
dependencies: ["ImportsImportsAPublicly"]
),
.target(
name: "ModuleA",
dependencies: [
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
]
),
.target(
name: "ImportsAPublicly",
dependencies: [
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.target(name: "ModuleA"),
]
),
.target(
name: "ImportsImportsAPublicly",
dependencies: [
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.target(name: "ImportsAPublicly"),
]
),
]
)
7 changes: 7 additions & 0 deletions CompileTests/MultiModule/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CompileTests/MultiModule

This is a test case that uses sources generated into multiple modules and
ensures the generated code compiles with the cross modules references.

This can't use the SwiftPM Plugin as that currently doesn't have support for the
module mappings.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: Sources/ImportsAPublicly/imports_a_publicly.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/

import Foundation
import SwiftProtobuf

import ModuleA

// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _3: SwiftProtobuf.ProtobufAPIVersion_3 {}
typealias Version = _3
}

public struct ImportsAPublicly: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.

public var a: ModuleA.A {
get {return _a ?? ModuleA.A()}
set {_a = newValue}
}
/// Returns true if `a` has been explicitly set.
public var hasA: Bool {return self._a != nil}
/// Clears the value of `a`. Subsequent reads from it will return its default value.
public mutating func clearA() {self._a = nil}

public var e: ModuleA.E {
get {return _e ?? .unset}
set {_e = newValue}
}
/// Returns true if `e` has been explicitly set.
public var hasE: Bool {return self._e != nil}
/// Clears the value of `e`. Subsequent reads from it will return its default value.
public mutating func clearE() {self._e = nil}

public var unknownFields = SwiftProtobuf.UnknownStorage()

public init() {}

fileprivate var _a: ModuleA.A? = nil
fileprivate var _e: ModuleA.E? = nil
}

// MARK: - Code below here is support for the SwiftProtobuf runtime.

extension ImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "ImportsAPublicly"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
11: .same(proto: "a"),
12: .same(proto: "e"),
]

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 11: try { try decoder.decodeSingularMessageField(value: &self._a) }()
case 12: try { try decoder.decodeSingularEnumField(value: &self._e) }()
default: break
}
}
}

public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = self._a {
try visitor.visitSingularMessageField(value: v, fieldNumber: 11)
} }()
try { if let v = self._e {
try visitor.visitSingularEnumField(value: v, fieldNumber: 12)
} }()
try unknownFields.traverse(visitor: &visitor)
}

public static func ==(lhs: ImportsAPublicly, rhs: ImportsAPublicly) -> Bool {
if lhs._a != rhs._a {return false}
if lhs._e != rhs._e {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: Sources/ImportsImportsAPublicly/imports_imports_a_publicly.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/

import Foundation
import SwiftProtobuf

import ImportsAPublicly
import ModuleA

// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _3: SwiftProtobuf.ProtobufAPIVersion_3 {}
typealias Version = _3
}

public struct ImportsImportsAPublicly: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.

public var a: ModuleA.A {
get {return _a ?? ModuleA.A()}
set {_a = newValue}
}
/// Returns true if `a` has been explicitly set.
public var hasA: Bool {return self._a != nil}
/// Clears the value of `a`. Subsequent reads from it will return its default value.
public mutating func clearA() {self._a = nil}

public var e: ModuleA.E {
get {return _e ?? .unset}
set {_e = newValue}
}
/// Returns true if `e` has been explicitly set.
public var hasE: Bool {return self._e != nil}
/// Clears the value of `e`. Subsequent reads from it will return its default value.
public mutating func clearE() {self._e = nil}

public var unknownFields = SwiftProtobuf.UnknownStorage()

public init() {}

fileprivate var _a: ModuleA.A? = nil
fileprivate var _e: ModuleA.E? = nil
}

// MARK: - Code below here is support for the SwiftProtobuf runtime.

extension ImportsImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "ImportsImportsAPublicly"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
21: .same(proto: "a"),
22: .same(proto: "e"),
]

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 21: try { try decoder.decodeSingularMessageField(value: &self._a) }()
case 22: try { try decoder.decodeSingularEnumField(value: &self._e) }()
default: break
}
}
}

public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = self._a {
try visitor.visitSingularMessageField(value: v, fieldNumber: 21)
} }()
try { if let v = self._e {
try visitor.visitSingularEnumField(value: v, fieldNumber: 22)
} }()
try unknownFields.traverse(visitor: &visitor)
}

public static func ==(lhs: ImportsImportsAPublicly, rhs: ImportsImportsAPublicly) -> Bool {
if lhs._a != rhs._a {return false}
if lhs._e != rhs._e {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
117 changes: 117 additions & 0 deletions CompileTests/MultiModule/Sources/ModuleA/a.pb.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: Sources/ModuleA/a.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/

import Foundation
import SwiftProtobuf

// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _3: SwiftProtobuf.ProtobufAPIVersion_3 {}
typealias Version = _3
}

public enum E: SwiftProtobuf.Enum {
public typealias RawValue = Int
case unset // = 0
case a // = 1
case b // = 2

public init() {
self = .unset
}

public init?(rawValue: Int) {
switch rawValue {
case 0: self = .unset
case 1: self = .a
case 2: self = .b
default: return nil
}
}

public var rawValue: Int {
switch self {
case .unset: return 0
case .a: return 1
case .b: return 2
}
}

}

public struct A: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.

public var e: E {
get {return _e ?? .unset}
set {_e = newValue}
}
/// Returns true if `e` has been explicitly set.
public var hasE: Bool {return self._e != nil}
/// Clears the value of `e`. Subsequent reads from it will return its default value.
public mutating func clearE() {self._e = nil}

public var unknownFields = SwiftProtobuf.UnknownStorage()

public init() {}

fileprivate var _e: E? = nil
}

// MARK: - Code below here is support for the SwiftProtobuf runtime.

extension E: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "E_UNSET"),
1: .same(proto: "E_A"),
2: .same(proto: "E_B"),
]
}

extension A: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "A"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "e"),
]

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self._e) }()
default: break
}
}
}

public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = self._e {
try visitor.visitSingularEnumField(value: v, fieldNumber: 1)
} }()
try unknownFields.traverse(visitor: &visitor)
}

public static func ==(lhs: A, rhs: A) -> Bool {
if lhs._e != rhs._e {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
Loading

0 comments on commit 2a383b6

Please sign in to comment.