Maven自动编译Protocol Buffers

本文主要解决maven自动编译.proto文件的问题

难题

  • 多平台protoc:团队中有人使用mac 有人使用windows开发,编译使用的protoc是二进制程序,根据不同操作系统有不同的版本。
  • 按需编译:有一些模块(module)包含有proto目录,需要编译其中的.proto文件。另一些模块不包含proto目录,不需要编译。
  • 跨模块引用:有一些模块需要引用到其他模块的proto目录。

下载protoc到本地

定义全局属性:保持版本和路径一致

<protobuf.input.directory>${project.basedir}/src/main/protoprotobuf.input.directory>
<protobuf.output.directory>src/main/gen-javaprotobuf.output.directory>
<protobuf.protoc.path>${settings.localRepository}/protocprotobuf.protoc.path>
<protobuf.version>3.2.0protobuf.version>

引用protobuf-API:编译成的.java文件需要依赖这些库文件

<dependency>
    <groupId>com.google.protobufgroupId>
    <artifactId>protobuf-javaartifactId>
    <version>${protobuf.version}version>
dependency>

识别系统类型:区分Windows/Linux/OS X 不同系统,区分32/64-bit,识别结果会写入到变量里面

<build>
    <extensions>
        
        <extension>
            <groupId>kr.motd.mavengroupId>
            <artifactId>os-maven-pluginartifactId>
            <version>1.4.1.Finalversion>
        extension>
    extensions>
build>

自动下载protoc到本地目录


<plugin>
    <groupId>org.apache.maven.pluginsgroupId>
    <artifactId>maven-dependency-pluginartifactId>
    <version>2.10version>
    <executions>
        <execution>
            <id>copy-protocid>
            <phase>generate-sourcesphase>
            <goals>
                <goal>copygoal>
            goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>com.google.protobufgroupId>
                        <artifactId>protocartifactId>
                        <version>${protobuf.version}version>
                        <classifier>${os.detected.classifier}classifier>
                        <type>exetype>
                        <overWrite>falseoverWrite>
                        <outputDirectory>${protobuf.protoc.path}outputDirectory>
                    artifactItem>
                artifactItems>
            configuration>
        execution>
    executions>
plugin>

编译.proto文件

  • 使用maven-antrun的if语法,可以判断目录是否存在。如果目录存在,才开始编译工作。
  • 在命令行调用protoc文件的时候,可以加入多个-I参数引用多个路径。
  • 这里我们利用maven-antrun的propertyregex 工具,进行字符串替换。需要跨模块引用时,只需覆写protobuf.input.directory属性,多个目录之间用分号间隔。
<propertyregex property="proto.include"
        input="${protobuf.input.directory}"
        regexp=";"
        replace=" -I"
        defaultvalue="${protobuf.input.directory}"
        global="true" />

注意:
1. 插件依赖: if语法需要依赖插件ant-contrib:ant-contrib
2. 插件依赖: propertyregex工具需要引用 org.apache.ant:ant-apache-regexp
3. propertyregex 在匹配失败(regexp一个都没有匹配到)的时候,不会设置property;因此需要加入defaultvalue 设置成源串。

最后我们写成插件就是这样子:

<plugin>
    <artifactId>maven-antrun-pluginartifactId>
    <dependencies>
        <dependency>
            <groupId>ant-contribgroupId>
            <artifactId>ant-contribartifactId>
            <version>1.0b3version>
            <exclusions>
                <exclusion>
                    <groupId>antgroupId>
                    <artifactId>antartifactId>
                exclusion>
            exclusions>
        dependency>
        <dependency>
            <groupId>org.apache.antgroupId>
            <artifactId>ant-apache-regexpartifactId>
            <version>1.8.2version>
        dependency>
        <dependency>
            <groupId>org.apache.antgroupId>
            <artifactId>ant-nodepsartifactId>
            <version>1.8.1version>
        dependency>
    dependencies>
    <executions>
        <execution>
            <id>compile-protocid>
            <phase>generate-sourcesphase>
            <configuration>
                <tasks>
                    <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
                    <available property="isExist" file="src/main/proto" type="dir"/>
                    <if>
                        <equals arg1="${isExist}" arg2="true"/>
                        <then>
                            <property name="protoc.filename"
                                      value="protoc-${protobuf.version}-${os.detected.classifier}.exe"/>
                            <property name="protoc.filepath"
                                      value="${protobuf.protoc.path}/${protoc.filename}"/>
                            <propertyregex property="proto.include"
                                           input="${protobuf.input.directory}"
                                           regexp=";"
                                           replace=" -I"
                                           defaultvalue="${protobuf.input.directory}"
                                           global="true" />
                            <chmod file="${protoc.filepath}" perm="ugo+rx"/>

                            <path id="proto.path">
                                <fileset dir="src/main/proto">
                                    <include name="**/*.proto"/>
                                fileset>
                            path>

                            <mkdir dir="src/main/gen-java"/>
                            <pathconvert pathsep=" " property="proto.files" refid="proto.path"/>
                            <exec executable="${protoc.filepath}" failonerror="true">
                                <arg value="--java_out=${protobuf.output.directory}"/>
                                <arg value="-I${proto.include}"/>
                                <arg line="${proto.files}"/>
                            exec>
                        then>
                    if>
                tasks>
                <sourceRoot>${protobuf.output.directory}sourceRoot>
            configuration>
            <goals>
                <goal>rungoal>
            goals>
        execution>
    executions>
plugin>

加入到source

把新生成的gen-java目录加入到项目的source集合中,一起参与编译。


<plugin>
    <groupId>org.codehaus.mojogroupId>
    <artifactId>build-helper-maven-pluginartifactId>
    <version>1.7version>
    <executions>
        <execution>
            <id>add-classesid>
            <phase>generate-sourcesphase>
            <goals>
                <goal>add-sourcegoal>
            goals>
            <configuration>
                <sources>
                    <source>${protobuf.output.directory}source>
                sources>
            configuration>
        execution>
    executions>
plugin>

解决protobuf-API版本冲突

有的时候,我们会遇到protobuf版本冲突。
比如,我们的工程用到protobuf3.2版本,而引用到的一些库用到了protobuf 2.5版本。两个版本的一些api冲突,可能造成意想不到的问题。我们可以利用shade插件,把protobuf-API全部替换成私有的包名,避免命名冲突。


<plugin>
    <groupId>org.apache.maven.pluginsgroupId>
    <artifactId>maven-shade-pluginartifactId>
    <version>2.4.2version>
    <executions>
        <execution>
            <phase>packagephase>
            <goals>
                <goal>shadegoal>
            goals>
            <configuration>
                <relocations>
                    <relocation>
                        <pattern>com.google.protobufpattern>
                        <shadedPattern>${project.groupId}.${project.artifactId}.shaded.protobufshadedPattern>
                    relocation>
                relocations>
            configuration>
        execution>
    executions>
plugin>

在项目中,需要把

import com.google.protobuf.*;

替换成

import groupId.artifactId.shaded.protobuf.*;

参考:Compiling Protocol Buffers Sources in Maven

你可能感兴趣的:(maven实战)