From 6f6f8376d249a2143fa178a9037e4bce688a00d0 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 14 May 2025 11:03:54 +0100 Subject: [PATCH 1/5] Set version info using info from the package context Motivation: The version for the package included in protoc-gen-grpc-swift is updated manually and is currently incorrect. We can get the appropriate information from the context provided by SwiftPM. Modifications: - Add a C shim module which provides a version string from the package context Result: - Version string is kept up-to-date - Resolves #60 --- Package.swift | 21 ++++++++++++++++++ Sources/CGRPCProtobuf/CGRPCProtobuf.c | 21 ++++++++++++++++++ Sources/CGRPCProtobuf/include/CGRPCProtobuf.h | 22 +++++++++++++++++++ Sources/protoc-gen-grpc-swift/Version.swift | 21 +++--------------- 4 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 Sources/CGRPCProtobuf/CGRPCProtobuf.c create mode 100644 Sources/CGRPCProtobuf/include/CGRPCProtobuf.h diff --git a/Package.swift b/Package.swift index 73f9cad..5254bf8 100644 --- a/Package.swift +++ b/Package.swift @@ -54,12 +54,26 @@ let defaultSwiftSettings: [SwiftSetting] = [ .enableUpcomingFeature("MemberImportVisibility"), ] +extension Context { + fileprivate static var versionString: String { + guard let git = Self.gitInformation else { return "" } + + if let tag = git.currentTag { + return tag + } else { + let suffix = git.hasUncommittedChanges ? " (modified)" : "" + return git.currentCommit + suffix + } + } +} + let targets: [Target] = [ // protoc plugin for grpc-swift .executableTarget( name: "protoc-gen-grpc-swift", dependencies: [ .target(name: "GRPCProtobufCodeGen"), + .target(name: "CGRPCProtobuf"), .product(name: "GRPCCodeGen", package: "grpc-swift"), .product(name: "SwiftProtobuf", package: "swift-protobuf"), .product(name: "SwiftProtobufPluginLibrary", package: "swift-protobuf"), @@ -110,6 +124,13 @@ let targets: [Target] = [ swiftSettings: defaultSwiftSettings ), + .target( + name: "CGRPCProtobuf", + cSettings: [ + .define("CGRPC_GRPC_SWIFT_PROTOBUF_VERSION", to: "\"\(Context.versionString)\"") + ] + ), + // Code generator build plugin .plugin( name: "GRPCProtobufGenerator", diff --git a/Sources/CGRPCProtobuf/CGRPCProtobuf.c b/Sources/CGRPCProtobuf/CGRPCProtobuf.c new file mode 100644 index 0000000..cac19a9 --- /dev/null +++ b/Sources/CGRPCProtobuf/CGRPCProtobuf.c @@ -0,0 +1,21 @@ +/* + * Copyright 2025, gRPC 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. + */ + +#include "CGRPCProtobuf.h" + +const char *cgrprc_grpc_swift_protobuf_version() { + return CGRPC_GRPC_SWIFT_PROTOBUF_VERSION; +} diff --git a/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h b/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h new file mode 100644 index 0000000..15d393f --- /dev/null +++ b/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h @@ -0,0 +1,22 @@ +/* + * Copyright 2025, gRPC 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. + */ + +#ifndef CGRPC_PROTOBUF_H_ +#define CGRPC_PROTOBUF_H_ + +const char *cgrprc_grpc_swift_protobuf_version(); + +#endif // CGRPC_PROTOBUF_H_ diff --git a/Sources/protoc-gen-grpc-swift/Version.swift b/Sources/protoc-gen-grpc-swift/Version.swift index 7df4cd6..66002e8 100644 --- a/Sources/protoc-gen-grpc-swift/Version.swift +++ b/Sources/protoc-gen-grpc-swift/Version.swift @@ -14,26 +14,11 @@ * limitations under the License. */ -internal enum Version { - /// The major version. - internal static let major = 1 - - /// The minor version. - internal static let minor = 0 - - /// The patch version. - internal static let patch = 0 - - /// Any additional label. - internal static let label = "development" +private import CGRPCProtobuf +internal enum Version { /// The version string. internal static var versionString: String { - let version = "\(Self.major).\(Self.minor).\(Self.patch)" - if Self.label.isEmpty { - return version - } else { - return version + "-" + Self.label - } + String(cString: cgrprc_grpc_swift_protobuf_version()) } } From 1fcd91b1b843688b6c9890bd0da188396b8c877d Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 14 May 2025 12:42:40 +0100 Subject: [PATCH 2/5] fix license headers --- Sources/CGRPCProtobuf/CGRPCProtobuf.c | 28 +++++++++---------- Sources/CGRPCProtobuf/include/CGRPCProtobuf.h | 28 +++++++++---------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/Sources/CGRPCProtobuf/CGRPCProtobuf.c b/Sources/CGRPCProtobuf/CGRPCProtobuf.c index cac19a9..e9f454b 100644 --- a/Sources/CGRPCProtobuf/CGRPCProtobuf.c +++ b/Sources/CGRPCProtobuf/CGRPCProtobuf.c @@ -1,18 +1,16 @@ -/* - * Copyright 2025, gRPC 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. - */ +// Copyright 2025, gRPC 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. #include "CGRPCProtobuf.h" diff --git a/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h b/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h index 15d393f..46f752c 100644 --- a/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h +++ b/Sources/CGRPCProtobuf/include/CGRPCProtobuf.h @@ -1,18 +1,16 @@ -/* - * Copyright 2025, gRPC 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. - */ +// Copyright 2025, gRPC 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. #ifndef CGRPC_PROTOBUF_H_ #define CGRPC_PROTOBUF_H_ From 379f3b6f3d133c3155b12953074ca683e3f93bde Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 14 May 2025 14:05:45 +0100 Subject: [PATCH 3/5] Workaround SwiftPM --- .github/workflows/pull_request.yml | 2 + Package.swift | 67 ++++++++++++++------- Sources/protoc-gen-grpc-swift/Version.swift | 6 ++ 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 186fa22..073ea56 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -11,6 +11,8 @@ jobs: uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main with: license_header_check_project_name: "gRPC" + # See Package.swift for an explanation of this. + linux_pre_build_command: "export GRPC_SWIFT_PROTOBUF_NO_VERSION=1" grpc-soundness: name: Soundness diff --git a/Package.swift b/Package.swift index 5254bf8..5867ecd 100644 --- a/Package.swift +++ b/Package.swift @@ -54,26 +54,12 @@ let defaultSwiftSettings: [SwiftSetting] = [ .enableUpcomingFeature("MemberImportVisibility"), ] -extension Context { - fileprivate static var versionString: String { - guard let git = Self.gitInformation else { return "" } - - if let tag = git.currentTag { - return tag - } else { - let suffix = git.hasUncommittedChanges ? " (modified)" : "" - return git.currentCommit + suffix - } - } -} - -let targets: [Target] = [ +var targets: [Target] = [ // protoc plugin for grpc-swift .executableTarget( name: "protoc-gen-grpc-swift", dependencies: [ .target(name: "GRPCProtobufCodeGen"), - .target(name: "CGRPCProtobuf"), .product(name: "GRPCCodeGen", package: "grpc-swift"), .product(name: "SwiftProtobuf", package: "swift-protobuf"), .product(name: "SwiftProtobufPluginLibrary", package: "swift-protobuf"), @@ -124,12 +110,6 @@ let targets: [Target] = [ swiftSettings: defaultSwiftSettings ), - .target( - name: "CGRPCProtobuf", - cSettings: [ - .define("CGRPC_GRPC_SWIFT_PROTOBUF_VERSION", to: "\"\(Context.versionString)\"") - ] - ), // Code generator build plugin .plugin( @@ -164,6 +144,51 @@ let targets: [Target] = [ ), ] +// ------------------------------------------------------------------------------------------------- + +extension Context { + fileprivate static var versionString: String { + guard let git = Self.gitInformation else { return "" } + + if let tag = git.currentTag { + return tag + } else { + let suffix = git.hasUncommittedChanges ? " (modified)" : "" + return git.currentCommit + suffix + } + } + + fileprivate static var buildCGRPCProtobuf: Bool { + let noVersion = Context.environment.keys.contains("GRPC_SWIFT_PROTOBUF_NO_VERSION") + return !noVersion + } +} + +// Having a C module as a transitive dependency of a plugin seems to trip up the API breakage +// checking tool. See also https://github.com/swiftlang/swift-package-manager/issues/8081 +// +// The CGRPCProtobuf module (which only includes package version information) is conditionally +// compiled and included based on an environment variable. This is set in CI only for the API +// breakage checking job to avoid tripping up SwiftPM. +if Context.buildCGRPCProtobuf { + targets.append( + .target( + name: "CGRPCProtobuf", + cSettings: [ + .define("CGRPC_GRPC_SWIFT_PROTOBUF_VERSION", to: "\"\(Context.versionString)\"") + ] + ) + ) + + for target in targets { + if target.name == "protoc-gen-grpc-swift" { + target.dependencies.append(.target(name: "CGRPCProtobuf")) + } + } +} + +// ------------------------------------------------------------------------------------------------- + let package = Package( name: "grpc-swift-protobuf", platforms: [ diff --git a/Sources/protoc-gen-grpc-swift/Version.swift b/Sources/protoc-gen-grpc-swift/Version.swift index 66002e8..66dd5a5 100644 --- a/Sources/protoc-gen-grpc-swift/Version.swift +++ b/Sources/protoc-gen-grpc-swift/Version.swift @@ -14,11 +14,17 @@ * limitations under the License. */ +#if canImport(CGRPCProtobuf) private import CGRPCProtobuf +#endif internal enum Version { /// The version string. internal static var versionString: String { + #if canImport(CGRPCProtobuf) String(cString: cgrprc_grpc_swift_protobuf_version()) + #else + "unknown" + #endif } } From b857d7df683d62345ecfc00a15d6389d4ae0e2fd Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 14 May 2025 17:19:50 +0100 Subject: [PATCH 4/5] format --- Package.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Package.swift b/Package.swift index 5867ecd..978748e 100644 --- a/Package.swift +++ b/Package.swift @@ -110,7 +110,6 @@ var targets: [Target] = [ swiftSettings: defaultSwiftSettings ), - // Code generator build plugin .plugin( name: "GRPCProtobufGenerator", From 4d1a436ea2cea905da3568b23fdb665b86d39e91 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 14 May 2025 17:24:56 +0100 Subject: [PATCH 5/5] api break 2 --- .github/workflows/pull_request.yml | 5 +++-- .github/workflows/soundness.yml | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 073ea56..cdbf6de 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -11,8 +11,9 @@ jobs: uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main with: license_header_check_project_name: "gRPC" - # See Package.swift for an explanation of this. - linux_pre_build_command: "export GRPC_SWIFT_PROTOBUF_NO_VERSION=1" + # This is done by a similar job defined in soundness.yml. It needs to be + # separate in order to export an environment variable. + api_breakage_check_enabled: false grpc-soundness: name: Soundness diff --git a/.github/workflows/soundness.yml b/.github/workflows/soundness.yml index d880e14..c26dd73 100644 --- a/.github/workflows/soundness.yml +++ b/.github/workflows/soundness.yml @@ -35,3 +35,28 @@ jobs: - name: Check generated code run: | ./dev/check-generated-code.sh + + api-breakage-check: + name: API breakage check + runs-on: ubuntu-latest + container: + image: swift:latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 # Fetching tags requires fetch-depth: 0 (https://github.com/actions/checkout/issues/1471) + - name: Mark the workspace as safe + # https://github.com/actions/checkout/issues/766 + run: git config --global --add safe.directory ${GITHUB_WORKSPACE} + - name: Run API breakage check + shell: bash + # See package.swift for why we set GRPC_SWIFT_PROTOBUF_NO_VERSION=1 + run: | + export GRPC_SWIFT_PROTOBUF_NO_VERSION=1 + + git fetch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} ${GITHUB_BASE_REF}:pull-base-ref + BASELINE_REF='pull-base-ref' + echo "Using baseline: $BASELINE_REF" + swift package diagnose-api-breaking-changes "$BASELINE_REF"