Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code generation package plugins #26

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Code generation package plugins #26

wants to merge 1 commit into from

Conversation

rnro
Copy link
Collaborator

@rnro rnro commented Dec 24, 2024

Overview

  • New build plugin to generate gRPC services and protobuf messages
  • New command plugin
    e.g. swift package generate-grpc-code-from-protos Sources/Protos/HelloWorld.proto
  • Integration tests

This PR also includes a change to protoc-gen-grpc-swift itself:

  • Generate an empty file if no gRPC services are found in the protobuf definition file. This is required by the build plugin which needs deterministic outputs and matches the behavior of protoc-gen-swift. A separate PR to grpc-swift makes these files more clearly intentional.

Build Plugin

The SwiftPM build plugin will locate protobuf files in the Sources directory (with the extension .proto) and attempt to invoke both the protoc-gen-swift and protoc-gen-grpc-swift protoc plugins on them to automatically generate Swift source. Behavior can be modified by specifying one or more configuration files (named grpc-swift-config.json).

Configuration

  • The configuration file is JSON which may contain the following entries.
/// The configuration of the plugin.
struct ConfigurationFile: Codable {
  enum Visibility: String, Codable {            /// The visibility of the generated files.
    case `internal`                             /// The generated files should have `internal` access level.
    case `public`                               /// The generated files should have `public` access level.
    case `package`                              /// The generated files should have `package` access level.
  }

  var visibility: Visibility?                   /// The visibility of the generated files.
  var server: Bool?                             /// Whether server code is generated.
  var client: Bool?                             /// Whether client code is generated.
  var message: Bool?                            /// Whether message code is generated.
  var protoPathModuleMappings: String?          /// Path to module map .asciipb file.
  var useAccessLevelOnImports: Bool?            /// Whether imports should have explicit access levels.

  /// Specify the directory in which to search for
  /// imports. May be specified multiple times;
  /// directories will be searched in order.
  /// The target source directory is always appended
  /// to the import paths.
  var importPaths: [String]?

  var protocPath: String?                       /// The path to the `protoc` binary. If this is not set, SPM will try to find the tool itself.
}
  • For a given protobuf definition file the tool will search for configuration files in the same and all parent directories and will use the file lowest in the hierarchy.
  • Most configuration if not specified will use the protoc-plugin's own defaults.

Adoption

Users must add the plugin to any target which wishes to make use of it

  targets: [
    .executableTarget(
      name: "plugin-adopter",
      dependencies: [
        // ...
      ],
      plugins: [
        .plugin(name: "GRPCGeneratorPlugin", package: "grpc-swift-protobuf")
      ]
    )
  ]

Command Plugin

  • The command plugin may be invoked as described in the help output.
> swift package generate-grpc-code-from-protos --help
Usage: swift package generate-grpc-code-from-protos [flags] [input files]

Flags:

  --visibility                    The visibility of the generated files.
  --server                        Whether server code is generated.
  --client                        Whether client code is generated.
  --message                       Whether message code is generated.
  --file-naming                   The naming of output files with respect to the path of the source file.
  --proto-path-module-mappings    Path to module map .asciipb file.
  --use-access-level-on-imports   Whether imports should have explicit access levels.
  --import-path                   The directory in which to search for imports.
  --protoc-path                   The path to the `protoc` binary.
  --output                        The path into which the generated source files are created.
  --dry-run                       Print but do not execute the protoc commands.

  --help                          Print this help.
  • When executing, the command prints the protoc invocations it uses for ease of debugging. The --dry-run flag can be supplied for this purpose or so that they may be extracted and used separately e.g. in a script.
  • If no protoc path is supplied then SwiftPM will attempt to locate it.
  • If no output directory is supplied then generated files are placed in the working directory.
  • Most flags if not specified will use the protoc-plugin's own defaults.

@rnro rnro force-pushed the package_plugins branch 6 times, most recently from c911f1a to 454684d Compare December 24, 2024 16:34
@rnro rnro added the 🆕 semver/minor Adds new public API. label Jan 3, 2025
@rnro rnro changed the title Package plugins Code generation package plugins Jan 3, 2025
Motivation:

To make code generation more convenient for adopters.

Modifications:

* New build plugin to generate gRPC services and protobuf messages
* New command plugin
  e.g. `swift package generate-grpc-code-from-protos Sources/Protos/HelloWorld.proto`
* Integration tests (incomplete)

This PR also includes a change to `protoc-gen-grpc-swift` itself:
* Generate an empty file if no gRPC services are found in the protobuf definition file. This is required by the build plugin which needs deterministic outputs and matches the behavior of `protoc-gen-swift`.

Result:

* Users will be able to make use of the build and command plugins.
* `protoc-gen-grpc-swift` will Generate an empty file if no gRPC services are found in the protobuf definition file.
@rnro rnro force-pushed the package_plugins branch from d17c933 to 0494be6 Compare January 3, 2025 16:11
@rnro rnro marked this pull request as ready for review January 3, 2025 16:18
@rnro rnro requested a review from glbrntt January 3, 2025 16:18
Copy link
Collaborator

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @rnro, really excited for this. To make the review a bit easier could you split it up into multiple PRs? I think we probably want the following PRs at a minimum:

  1. Changes to protoc-gen-grpc-swift
  2. Build plugin
  3. Command plugin
  4. Integration tests

glbrntt pushed a commit that referenced this pull request Jan 7, 2025
This change is broken out of
#26

### Motivation

To prepare for use in a SwiftPM build plugin which requires
deterministic output files and to match the behavior of
`proto-gen-swift` we should generate a source file even if no
definitions are found.

### Modifications:

* We no longer return early in the case of no definitions being found.
* Add empty proto file test
* Add a preamble to `foo-messages.proto`
* Don't unconditionally add a dependency on `GRPCProtobuf` if there are
no services
* Add the new test case code generation to `dev/protos/generate.sh`
* Depend on `grpc-swift` `main` to pick up empty file generation
changes.

### Result:

We will generate a Swift source file even if no definitions are found.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🆕 semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants