You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多组Protobuf生成独立Descriptor文件的Gradle构建配置问题

解决Gradle-Protobuf分目录生成独立Descriptor文件的方案

核心思路

Gradle官方protobuf插件默认会合并所有srcDir下的Proto文件生成单一Descriptor,要实现子目录独立生成.dsc,需要为每个子目录创建独立的protoc执行任务,绕过插件的默认合并逻辑。

具体配置(Groovy DSL)

plugins {
    id 'com.google.protobuf' version '0.9.4'
    id 'java'
}

// 禁用默认的Java类生成任务(只保留Descriptor生成能力)
protobuf {
    protoc {
        // 让Gradle自动下载protoc,无需手动安装
        artifact = "com.google.protobuf:protoc:3.25.3"
    }
    generateProtoTasks {
        all().each { task ->
            task.builtins.remove java
        }
    }
}

// 遍历descriptors目录下的所有子文件夹,生成独立任务
def descriptorsRoot = file('descriptors')
descriptorsRoot.eachDir { subDir ->
    def taskName = "generate${subDir.name.capitalize()}Descriptor"
    def outputDsc = file("${buildDir}/descriptors/${subDir.name}.dsc")

    task(taskName, type: Exec) {
        commandLine protocExecutable,
            "--descriptor_set_out=${outputDsc.absolutePath}",
            "--proto_path=${descriptorsRoot.absolutePath}",
            // 若有公共依赖的Proto目录,可追加--proto_path参数
            // "--proto_path=${file('common-protos').absolutePath}",
            subDir.listFiles({ f -> f.name.endsWith('.proto') } as FileFilter).collect { it.absolutePath }

        // 配置增量构建:仅当子目录Proto文件变化时重新执行
        outputs.file outputDsc
        inputs.dir subDir
    }

    // 将自定义任务绑定到build生命周期
    build.dependsOn taskName
}

// 获取插件下载的protoc可执行文件路径(避免手动指定)
def getProtocExecutable() {
    return tasks.named('protoc').get().executable.path
}

具体配置(Kotlin DSL)

plugins {
    id("com.google.protobuf") version "0.9.4"
    java
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.25.3"
    }
    generateProtoTasks {
        all().forEach { task ->
            task.builtins.remove("java")
        }
    }
}

val descriptorsRoot = file("descriptors")
descriptorsRoot.listFiles { file -> file.isDirectory }?.forEach { subDir ->
    val taskName = "generate${subDir.name.replaceFirstChar { it.uppercase() }}Descriptor"
    val outputDsc = file("${layout.buildDirectory.get()}/descriptors/${subDir.name}.dsc")

    tasks.register<Exec>(taskName) {
        val protocPath = tasks.named<Exec>("protoc").get().executable.path
        commandLine(
            protocPath,
            "--descriptor_set_out=${outputDsc.absolutePath}",
            "--proto_path=${descriptorsRoot.absolutePath}",
            *subDir.listFiles { f -> f.name.endsWith(".proto") }!!.map { it.absolutePath }.toTypedArray()
        )

        outputs.file(outputDsc)
        inputs.dir(subDir)
    }

    tasks.named("build") {
        dependsOn(taskName)
    }
}

关键说明

  1. 独立任务隔离:每个子目录对应一个generateXXXDescriptor任务,单独调用protoc生成专属.dsc文件,彻底避免合并导致的类名冲突和依赖问题。
  2. 增量构建支持:通过inputs.dir和outputs.file配置,Gradle会自动检测Proto文件变化,仅重新生成修改目录对应的Descriptor。
  3. protoc自动管理:无需手动安装protoc,插件会自动下载指定版本的可执行文件,保证环境一致性。

内容的提问来源于stack exchange,提问作者Styp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 20:45:45