From 5d6433e26a21929124ac89b28e3414c106994fde Mon Sep 17 00:00:00 2001 From: Kostiantyn Gominyuk Date: Mon, 16 Jun 2025 21:38:55 +0200 Subject: [PATCH] Split generated code into multiple files --- Sources/protoc-gen-swift/EnumGenerator.swift | 8 +- Sources/protoc-gen-swift/FileGenerator.swift | 104 ++++++++++-------- Sources/protoc-gen-swift/FilePrinter.swift | 27 +++++ .../protoc-gen-swift/MessageGenerator.swift | 33 ++++-- .../SwiftGeneratorPlugin.swift | 21 +++- 5 files changed, 133 insertions(+), 60 deletions(-) create mode 100644 Sources/protoc-gen-swift/FilePrinter.swift diff --git a/Sources/protoc-gen-swift/EnumGenerator.swift b/Sources/protoc-gen-swift/EnumGenerator.swift index adb6433d6..db84e06b5 100644 --- a/Sources/protoc-gen-swift/EnumGenerator.swift +++ b/Sources/protoc-gen-swift/EnumGenerator.swift @@ -113,15 +113,15 @@ class EnumGenerator { p.print("]") } - func generateRuntimeSupport(printer p: inout CodePrinter) { - p.print( + func generateRuntimeSupport(filePrinter fp: inout FilePrinter) { + fp.nameProviding.print( "", "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {" ) - p.withIndentation { p in + fp.nameProviding.withIndentation { p in generateProtoNameProviding(printer: &p) } - p.print("}") + fp.nameProviding.print("}") } /// Generates the cases or statics (for alias) for the values. diff --git a/Sources/protoc-gen-swift/FileGenerator.swift b/Sources/protoc-gen-swift/FileGenerator.swift index 7554cd6d7..ac75e9b42 100644 --- a/Sources/protoc-gen-swift/FileGenerator.swift +++ b/Sources/protoc-gen-swift/FileGenerator.swift @@ -23,8 +23,7 @@ class FileGenerator { private let generatorOptions: GeneratorOptions private let namer: SwiftProtobufNamer - var outputFilename: String { - let ext = ".pb.swift" + func outputFilename(_ ext: String) -> String { let pathParts = splitPath(pathname: fileDescriptor.name) switch generatorOptions.outputNaming { case .fullPath: @@ -52,7 +51,7 @@ class FileGenerator { /// Generate, if `errorString` gets filled in, then report error instead of using /// what written into `printer`. - func generateOutputFile(printer p: inout CodePrinter, errorString: inout String?) { + func generateOutputFile(filePrinter fp: inout FilePrinter, errorString: inout String?) { guard fileDescriptor.options.swiftPrefix.isEmpty || isValidSwiftIdentifier( @@ -64,20 +63,23 @@ class FileGenerator { "\(fileDescriptor.name) has an 'swift_prefix' that isn't a valid Swift identifier (\(fileDescriptor.options.swiftPrefix))." return } - p.print( - """ - // DO NOT EDIT. - // swift-format-ignore-file - // swiftlint:disable all - // - // Generated by the Swift generator plugin for the protocol buffer compiler. - // Source: \(fileDescriptor.name) - // - // For information on using the generated types, please see the documentation: - // https://github.com/apple/swift-protobuf/ + + fp.forEach { p in + p.print( + """ + // DO NOT EDIT. + // swift-format-ignore-file + // swiftlint:disable all + // + // Generated by the Swift generator plugin for the protocol buffer compiler. + // Source: \(fileDescriptor.name) + // + // For information on using the generated types, please see the documentation: + // https://github.com/apple/swift-protobuf/ - """ - ) + """ + ) + } // Attempt to bring over the comments at the top of the .proto file as // they likely contain copyrights/preamble/etc. @@ -106,7 +108,7 @@ class FileGenerator { if !comments.isEmpty { // If the was a leading or tailing comment it won't have a blank // line, after it, so ensure there is one. - p.print(comments, newlines: !comments.hasSuffix("\n\n")) + fp.main.print(comments, newlines: !comments.hasSuffix("\n\n")) } } @@ -115,17 +117,23 @@ class FileGenerator { var hasImports = false if fileDescriptor.needsFoundationImport { - p.print("\(generatorOptions.importDirective.snippet) Foundation") + fp.forEach { p in + p.print("\(generatorOptions.importDirective.snippet) Foundation") + } hasImports = true } if fileDescriptor.isBundledProto { - p.print( - "// 'import \(namer.swiftProtobufModuleName)' suppressed, this proto file is meant to be bundled in the runtime." - ) + fp.forEach { p in + p.print( + "// 'import \(namer.swiftProtobufModuleName)' suppressed, this proto file is meant to be bundled in the runtime." + ) + } hasImports = true } else if fileDefinesTypes { - p.print("\(generatorOptions.importDirective.snippet) \(namer.swiftProtobufModuleName)") + fp.forEach { p in + p.print("\(generatorOptions.importDirective.snippet) \(namer.swiftProtobufModuleName)") + } hasImports = true } @@ -135,25 +143,31 @@ class FileGenerator { reexportPublicImports: generatorOptions.visibility != .internal ) if !neededImports.isEmpty { - if hasImports { - p.print() + fp.forEach { p in + if hasImports { + p.print() + } + p.print(neededImports) } - p.print(neededImports) hasImports = true } // If there is nothing to generate, then just record that and be done (usually means // there just was one or more services). guard fileDefinesTypes else { - if hasImports { - p.print() + fp.forEach { p in + if hasImports { + p.print() + } + p.print("// This file contained no messages, enums, or extensions.") } - p.print("// This file contained no messages, enums, or extensions.") return } - p.print() - generateVersionCheck(printer: &p) + fp.forEach { p in + p.print() + generateVersionCheck(printer: &p) + } let extensionSet = ExtensionSetGenerator( @@ -178,54 +192,56 @@ class FileGenerator { } for e in enums { - e.generateMainEnum(printer: &p) + e.generateMainEnum(printer: &fp.main) } for m in messages { - m.generateMainStruct(printer: &p, parent: nil, errorString: &errorString) + m.generateMainStruct(printer: &fp.main, parent: nil, errorString: &errorString) } if !extensionSet.isEmpty { let pathParts = splitPath(pathname: fileDescriptor.name) let filename = pathParts.base + pathParts.suffix - p.print( + fp.main.print( "", "// MARK: - Extension support defined in \(filename)." ) // Generate the Swift Extensions on the Messages that provide the api // for using the protobuf extension. - extensionSet.generateMessageSwiftExtensions(printer: &p) + extensionSet.generateMessageSwiftExtensions(printer: &fp.main) // Generate a registry for the file. - extensionSet.generateFileProtobufExtensionRegistry(printer: &p) + extensionSet.generateFileProtobufExtensionRegistry(printer: &fp.main) // Generate the Extension's declarations (used by the two above things). // // This is done after the other two as the only time developers will need // these symbols is if they are manually building their own ExtensionMap; // so the others are assumed more interesting. - extensionSet.generateProtobufExtensionDeclarations(printer: &p) + extensionSet.generateProtobufExtensionDeclarations(printer: &fp.main) } let protoPackage = fileDescriptor.package let needsProtoPackage: Bool = !protoPackage.isEmpty && !messages.isEmpty if needsProtoPackage || !enums.isEmpty || !messages.isEmpty { - p.print( - "", - "// MARK: - Code below here is support for the SwiftProtobuf runtime." - ) - if needsProtoPackage { + fp.forEachRuntimeExtension { p in p.print( "", - "fileprivate let _protobuf_package = \"\(protoPackage)\"" + "// MARK: - Code below here is support for the SwiftProtobuf runtime." ) + if needsProtoPackage { + p.print( + "", + "fileprivate let _protobuf_package = \"\(protoPackage)\"" + ) + } } for e in enums { - e.generateRuntimeSupport(printer: &p) + e.generateRuntimeSupport(filePrinter: &fp) } for m in messages { - m.generateRuntimeSupport(printer: &p, file: self, parent: nil) + m.generateRuntimeSupport(filePrinter: &fp, file: self, parent: nil) } } } diff --git a/Sources/protoc-gen-swift/FilePrinter.swift b/Sources/protoc-gen-swift/FilePrinter.swift new file mode 100644 index 000000000..5fcc4e734 --- /dev/null +++ b/Sources/protoc-gen-swift/FilePrinter.swift @@ -0,0 +1,27 @@ +// Sources/protoc-gen-swift/MessageStorageDecision.swift +// +// Copyright (c) 2014 - 2016 Apple Inc. and the project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See LICENSE.txt for license information: +// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt +// +// ----------------------------------------------------------------------------- + +import SwiftProtobufPluginLibrary + +struct FilePrinter { + var main: CodePrinter + var hashable: CodePrinter + var nameProviding: CodePrinter + + mutating func forEach(_ body: (_ p: inout CodePrinter) -> Void) { + body(&main) + forEachRuntimeExtension(body) + } + + mutating func forEachRuntimeExtension(_ body: (_ p: inout CodePrinter) -> Void) { + body(&hashable) + body(&nameProviding) + } +} diff --git a/Sources/protoc-gen-swift/MessageGenerator.swift b/Sources/protoc-gen-swift/MessageGenerator.swift index 4af382f72..3fe58bfb2 100644 --- a/Sources/protoc-gen-swift/MessageGenerator.swift +++ b/Sources/protoc-gen-swift/MessageGenerator.swift @@ -205,12 +205,12 @@ class MessageGenerator { p.print("}") } - func generateRuntimeSupport(printer p: inout CodePrinter, file: FileGenerator, parent: MessageGenerator?) { - p.print( + func generateRuntimeSupport(filePrinter fp: inout FilePrinter, file: FileGenerator, parent: MessageGenerator?) { + fp.main.print( "", - "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)Message, \(namer.swiftProtobufModulePrefix)_MessageImplementationBase, \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {" + "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)Message {" ) - p.withIndentation { p in + fp.main.withIndentation { p in if let parent = parent { p.print( "\(visibility)static let protoMessageName: String = \(parent.swiftFullName).protoMessageName + \".\(descriptor.name)\"" @@ -222,7 +222,6 @@ class MessageGenerator { } else { p.print("\(visibility)static let protoMessageName: String = \"\(descriptor.name)\"") } - generateProtoNameProviding(printer: &p) if let storage = storage { p.print() storage.generateTypeDeclaration(printer: &p) @@ -235,17 +234,33 @@ class MessageGenerator { generateDecodeMessage(printer: &p) p.print() generateTraverse(printer: &p) - p.print() + } + fp.main.print("}") + + fp.hashable.print( + "", + "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)_MessageImplementationBase {" + ) + fp.hashable.withIndentation { p in generateMessageEquality(printer: &p) } - p.print("}") + fp.hashable.print("}") + + fp.nameProviding.print( + "", + "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {" + ) + fp.nameProviding.withIndentation { p in + generateProtoNameProviding(printer: &p) + } + fp.nameProviding.print("}") // Nested enums and messages for e in enums { - e.generateRuntimeSupport(printer: &p) + e.generateRuntimeSupport(filePrinter: &fp) } for m in messages { - m.generateRuntimeSupport(printer: &p, file: file, parent: self) + m.generateRuntimeSupport(filePrinter: &fp, file: file, parent: self) } } diff --git a/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift b/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift index 899bcd62c..4a7285dd9 100644 --- a/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift +++ b/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift @@ -35,14 +35,29 @@ struct SwiftGeneratorPlugin: CodeGenerator { var errorString: String? = nil for fileDescriptor in files { let fileGenerator = FileGenerator(fileDescriptor: fileDescriptor, generatorOptions: options) - var printer = CodePrinter(addNewlines: true) - fileGenerator.generateOutputFile(printer: &printer, errorString: &errorString) + var filePrinter = FilePrinter( + main: CodePrinter(addNewlines: true), + hashable: CodePrinter(addNewlines: true), + nameProviding: CodePrinter(addNewlines: true) + ) + fileGenerator.generateOutputFile(filePrinter: &filePrinter, errorString: &errorString) if let errorString = errorString { // If generating multiple files, scope the message with the file that triggered it. let fullError = files.count > 1 ? "\(fileDescriptor.name): \(errorString)" : errorString throw GenerationError.message(message: fullError) } - try generatorOutputs.add(fileName: fileGenerator.outputFilename, contents: printer.content) + try generatorOutputs.add( + fileName: fileGenerator.outputFilename(".pb.swift"), + contents: filePrinter.main.content + ) + try generatorOutputs.add( + fileName: fileGenerator.outputFilename(".pb.hashable.swift"), + contents: filePrinter.hashable.content + ) + try generatorOutputs.add( + fileName: fileGenerator.outputFilename(".pb.nameproviding.swift"), + contents: filePrinter.nameProviding.content + ) } }