Introduction

The Protobuf Maven Plugin is a modern Maven plugin that attempts to reduce the hassle needed to integrate Protobuf compilation into your build process.

Unlike existing Protobuf integrations, this plugin is able to pull the desired version of protoc directly from Google's releases on Maven Central. This means you do not need to update the version of this plugin to be able to pull in a newer version of protoc when it is released.

If your system is not directly supported by Google in the protoc releases they supply, you can instruct the plugin to instead invoke protoc from your system path directly.

For users who are writing GRPC services, this plugin can also support generating the GRPC stubs for you automatically.

In addition to generating Java sources, this plugin can also generate Kotlin sources.

Bugs and feature requests

Please raise any bugs or feature requests on the GitHub project for this plugin.

Detailed examples

If you need detailed working examples to use as reference, then the integration tests are a great place to start, since they are full working Maven projects.

If you wish to contribute additional test cases to show integration with custom plugins or more complicated use cases, then this is always welcome.

Usage reference

The following sections document basic usage of this plugin for several of the most popular use cases.

If you need the full reference for a parameters that are allowed, visit the goals page.

Minimum requirements

You will need to be using Maven 3.8.2 and Java 11 at a minimum.

Basic configuration

At the core, this plugin is designed to be fairly simple to use, and will attempt to resolve everything that you need automatically. All you need to do is provide the version of protoc to use.

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>${protobuf-maven-plugin.version}</version>

  <configuration>
    <protocVersion>4.27.1</protocVersion>
  </configuration>

  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Currently, the newest supported version of protoc is newest-protoc-version.

To generate test sources, use the generate-test goal instead.

Inputs will be read from src/main/protobuf (or src/test/protobuf for tests) and will be output to target/generated-sources/protobuf (or target/generated-test-sources/protobuf for tests). This can be overridden in the configuration block if needed.

The protoc version itself can be set via the <protocVersion> configuration parameter as shown above, or can be set via the protobuf.compiler.version property in your POM. It may optionally be totally overridden on the command line by passing -Dprotobuf.compiler.version=xxx, in which case the <protocVersion> and protobuf.compiler.version will be ignored. This is done to allow users who may have an incompatible system to be able to request a build using the $PATH-based protoc binary on their system (documented later in this page).

Dependencies

It is worth noting that you will need to include the protobuf-java dependency for the generated Java code to actually compile.

Ideally, you should use the same version for protobuf-java as you do for the protocVersion parameter. Doing this with a shared property will also allow tools like Dependabot to keep the compiler version up-to-date automatically.

<project>

  ...

  <properties>
    <protobuf.version>4.27.1</protobuf.version>
    <protobuf-maven-plugin.version>...</protobuf-maven-plugin.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>${protobuf.version}</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>io.github.ascopes</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>${protobuf-maven-plugin.version}</version>

        <configuration>
          <protocVersion>${protobuf.version}</protocVersion>
        </configuration>

        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

If you are using other types of output, you'll need different dependencies. The following table documents the most common ones that you'll run across.

Dependencies are listed as groupId:artifactId for brevity. Naming is not 100% consistent, so be sure to use exactly what is written below.

Configuration type Dependencies
Java protobuf messages
  • com.google.protobuf:protobuf-java
Java "lite" protobuf messages
  • com.google.protobuf:protobuf-javalite
Kotlin protobuf messages
  • com.google.protobuf:protobuf-kotlin
Kotlin "lite" protobuf messages
  • com.google.protobuf:protobuf-kotlin-lite
Java GRPC services
  • com.google.protobuf:protobuf-java
  • io.grpc:grpc-protobuf
  • io.grpc:grpc-stub
  • javax.annotation:javax.annotation-api
Kotlin GRPC services
  • com.google.protobuf:protobuf-kotlin
  • io.grpc:grpc-protobuf-kotlin
  • io.grpc:grpc-stub
  • javax.annotation:javax.annotation-api

v4.x versus v3.x

The v4.x versions of the protobuf libraries have breaking changes compared to the v3.x versions. This means that using the protoc binary corresponding to v4.x will not be compatible with libraries using v3.x of the protobuf libraries.

Ensure you are using the correct version for your project and requirements!

Importing protobuf definitions from other places

By default, this plugin will index all JARs that are dependencies for the current Maven project, just like you would expect when using Java code. This plugin considers any dependency that is marked with the compile, provided, or system scope (or additionally test if the generate-test goal is used).

If there are additional paths on the file system that you wish to add to the import path, then you can specify these using the importPaths parameter. Note that these will not be compiled, only made visible to the protobuf compiler.

Generating other language sources

The following languages are available. You can turn any combination on and off. If all are disabled then at least one plugin must be provided.

Language Parameter Default value Notes
C++ cppEnabled false
C# csharpEnabled false
Java javaEnabled true
Kotlin kotlinEnabled false Generates JVM Kotlin descriptors. You should also ensure javaEnabled is true.
Objective-C objcEnabled false
PHP phpEnabled false
Python pythonEnabled false
Python typeshed stubs pythonStubsEnabled false Enable this alongside pythonEnabled to generate MyPy-compatible typehint stubs.
Ruby rubyEnabled false
Rust rustEnabled false

Other languages (such as Scala, Clojure, etc) can be configured by using third-party plugins (see further down this page).

Note that all generated sources will be written to the same output directory, so you may want to configure multiple executions for each language to override the individual output directories.

It is also important to note that you need to provide a valid compiler or tooling to make use of the generared sources (other than Java). For example, Kotlin generation would require you to also configure the kotlin-maven-plugin.

Changing the input directories

If you do not want to use the default directory for your sources, you can override it in the plugin configuration:

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>
  
  <configuration>
    <sourceDirectories>
      <sourceDirectory>path/to/your/directory</sourceDirectory>
    </sourceDirectories>
    ...
  </configuration>
  
  ...
</plugin>

Multiple source directories can be specified if required.

Generating lightweight sources

If you are in a situation where you need lightweight and fast protobuf generated sources, you can opt in to generating “lite” sources only. These will omit all the metadata usually included within generated protobuf sources, at the cost of flexibility.

Refer to the protobuf documentation for more details on the pros and cons of doing this.

To enable this in the plugin, set the liteOnly parameter to true. By default, this is disabled as you usually do not need to worry about this.

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    <liteOnly>true</liteOnly>
    ...
  </configuration>

  ...
</plugin>

Customising the source for protoc

Using protoc from your system path

If you need to use the version of protoc that is installed on your system, specify the version as PATH.

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    <protocVersion>PATH</protocVersion>
  </configuration>

  ...
</plugin>

Using protoc from a specific path

You may wish to run protoc from a specific path on your file system. If you need to do this, you can provide a URL with the file scheme to reference it:

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    <protocVersion>file:///opt/protoc/protoc.exe</protocVersion>
  </configuration>
</plugin>

Using protoc from a remote server

If you have a protoc binary on a remote FTP or HTTP(S) server, you can provide the URL to download directly:

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    <protocVersion>ftp://company-server.internal/protoc/protoc.exe</protocVersion>
    <!-- or -->
    <protocVersion>http://company-server.internal/protoc/protoc.exe</protocVersion>
    <!-- or -->
    <protocVersion>https://company-server.internal/protoc/protoc.exe</protocVersion>
  </configuration>
</plugin>

This is not recommended outside specific use cases, and care should be taken to ensure the legitimacy and security of any URLs being provided prior to adding them.

Providing authentication details or proxy details is not supported at this time.

Additional plugins

If you wish to generate GRPC stubs, or outputs for other languages like Scala that are not already covered by the protoc executable, you can add custom plugins to your build.

Binary plugins

Binary plugins are OS-specific executables that are passed to protoc directly, and are the standard way of handling plugins with protoc.

Binary plugins from Maven Central

If the plugin you wish to use is on Maven Central or any other Maven repository, you can reference that plugin directly via the group ID, artifact ID, and version (like any other Maven artifact).

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    ...
    <binaryMavenPlugins>
      <binaryMavenPlugin>
        <groupId>io.grpc</groupId>
        <artifactId>protoc-gen-grpc-java</artifactId>
        <version>${grpc.version}</version>
      </binaryMavenPlugin>
    </binaryMavenPlugins>
  </configuration>

  ...
</plugin>

Each binaryMavenPlugin can take an optional options parameter which will be passed as an option to the plugin if specified.

Binary plugins from the system path

If you instead wish to read the executable from the system $PATH, then you can specify an executable name instead:

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    ...
    <binaryPathPlugins>
      <binaryPathPlugin>
        <name>protoc-gen-grpc-java</name>
      </binaryPathPlugin>
    </binaryPathPlugins>
  </configuration>

  ...
</plugin>

Each binaryPathPlugin can take an optional options parameter which will be passed as an option to the plugin if specified.

You can also mark these plugins as being optional by setting <optional>true</optional> on the individual plugin objects. This will prevent the Maven plugin from failing the build if the protoc plugin cannot be resolved on the system path. This is useful for specific cases where resources may only be available during CI builds but do not prevent the application being built locally.

Binary plugins from specific locations

In some situations, you may wish to download plugins directly from a URL or run them from a specific file system path:

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    ...
    <binaryUrlPlugins>
      <binaryUrlPlugin>
        <url>file:///opt/protoc/protoc-gen-grpc-java</url>
      </binaryUrlPlugin>
      <binaryUrlPlugin>
        <url>ftp://company-server.internal/some-other-plugin.exe</url>
      </binaryUrlPlugin>
      <binaryUrlPlugin>
        <url>https://some-website.net/downloads/my-cool-protoc-plugin.exe</url>
        <options>some-option=some-value</options>
      </binaryUrlPlugin>
    </binaryUrlPlugins>
  </configuration>

  ...
</plugin>

Any protocols supported by your JRE should be able to be used here, including:

  • file:
  • http:
  • https:
  • ftp:
  • jar: - this also works for ZIP files, and can be used to dereference files within the archive, e.g. jar:https://github.com/some-project/some-repo/releases/download/v1.1.1/plugin.zip!/plugin.exe, which would download https://github.com/some-project/some-repo/releases/download/v1.1.1/plugin.zip and internally extract plugin.exe from that archive.

Each binaryUrlPlugin can take an optional options parameter which will be passed as an option to the plugin if specified.

You can also mark these plugins as being optional by setting <optional>true</optional> on the individual plugin objects. This will prevent the Maven plugin from failing the build if the protoc plugin cannot be resolved. This is useful for specific cases where resources may only be available during CI builds but do not prevent the application being built locally. If set to optional, then any “not found” response provided by the underlying URL protocol will be ignored.

This is not recommended outside specific use cases, and care should be taken to ensure the legitimacy and security of any URLs being provided prior to adding them.

Providing authentication details or proxy details is not supported at this time.

Pure-Java plugins

If a protoc plugin is distributed as a platform-independent JAR archive rather than a native executable, you can instruct this Maven plugin to invoke the artifact as part of compilation. To do this, simply specify the jvmMavenPlugins configuration property, passing in a list of dependencies to execute.

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    ...
    <jvmMavenPlugins>
      <jvmMavenPlugin>
        <!-- Use the JAR that Salesforce distributes -->
        <groupId>com.salesforce.servicelibs</groupId>
        <artifactId>reactor-grpc</artifactId>
        <version>${reactor-grpc.version}</version>
      </jvmMavenPlugin>
    </jvmMavenPlugins>
  </configuration>

  ...
</plugin>

Each jvmMavenPlugin can take an optional options parameter which will be passed as an option to the plugin if specified.

Currently, you are required to be able to execute *.bat files on Windows, or have sh available on the system $PATH for any other platform.

Mixing plugins

Multiple plugins can be provided if needed. For example, if you are using the Salesforce Reactor GRPC libraries, then you can provide the following:

<plugin>
  <groupId>io.github.ascopes</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>...</version>

  <configuration>
    ...
    <binaryMavenPlugins>
      <binaryMavenPlugin>
        <groupId>io.grpc</groupId>
        <artifactId>protoc-gen-grpc-java</artifactId>
        <version>${grpc.version}</version>
      </binaryMavenPlugin>
      <binaryMavenPlugin>
        <!-- Use the native *.exe that Salesforce distributes -->
        <groupId>com.salesforce.servicelibs</groupId>
        <artifactId>reactor-grpc</artifactId>
        <version>${reactor-grpc.version}</version>
      </binaryMavenPlugin>
    </binaryMavenPlugins>
  </configuration>

  ...
</plugin>