Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gradle: Java/Kotlin, C++, ProtoBuf in the same project

I have a Java+Kotlin application, building with Gradle.

Some of its low-level functionality is provided by a separate C++ application.

The two applications communicate via a socket or pipe, using Protocol Buffers and gRPC.

Initially, I'd hoped to build all three (ProtoBuf generated code, Java application, C++ application) in one project, however the cpp-application and java conflict over certain tasks (compile/implementation/test?).

I've since split this into three projects:

/
 build.gradle
 settings.gradle
 cpp-app/
         build.gradle
         settings.gradle
         ...
 java-app/
          build.gradle
          settings.gradle
          ...
 protocol/
          build.gradle
          settings.gradle
          build/generated/source/proto/main/java/...   <-- Java generated code
          build/generated/source/proto/main/cpp/...    <-- C++ generated code
          ...

I have the protocol project successfully generating C++ and Java implementations.

How do I get the C++ and Java application projects to resolve and to use these outputs in their builds?

like image 263
Mark K Cowan Avatar asked Mar 20 '19 12:03

Mark K Cowan


1 Answers

I solved this while I was writing the question.

The gradle configuration files are shown below. There are a couple redundant blocks which are not needed for this example (or needed at all), however they achieve the objective.

/build.gradle

subprojects {
        group = 'com.whatever.your.group'
        version = '0.0.0'
        repositories {
                mavenCentral()
        }
}

/settings.gradle

rootProject.name = 'my-project'
include 'java-app'
include 'cpp-app'
include 'protocol'

/java-app/build.gradle

buildscript {
        repositories {
                mavenCentral()
        }
        dependencies {
                classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.20"
        }
}

plugins {
        // Java
        id 'maven'
        id 'idea'
        id 'application'
        id 'java'
        id 'org.jetbrains.kotlin.jvm' version '1.3.20'

        // ProtoBuf
        id 'com.google.protobuf' version '0.8.8'
}

description = """"""

sourceCompatibility = 8
targetCompatibility = 8

compileKotlin {
        kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
        kotlinOptions.jvmTarget = "1.8"
}

mainClassName = 'my.main.Class'

dependencies {
        protobuf project(':protocol') // <-- name of protobuf project
        compile project(':protocol')  // <-- name of protobuf project
        // We have "protobuf" and "compile", as "compile" brings in transitive dependencies

        testCompile 'junit:junit:4.12'

        implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.20"

        testImplementation "org.jetbrains.kotlin:kotlin-test:1.3.20"
        testImplementation "org.jetbrains.kotlin:kotlin-test-junit:1.3.20"
}

sourceSets {
        main {
                java {
                        srcDir "src/main/java"
                }
                kotlin {
                        srcDir "src/main/kotlin"
                }
        }
}

/java-app/settings.gradle

rootProject.name = 'java-app'

/cpp-app/build.gradle

plugins {
        id 'maven'
        id 'cpp'
}

description = """"""

project.tasks.build.dependsOn 'protocol'  // <-- name of protobuf project

model {
        components {
                main(NativeExecutableSpec) {
                        ...
                }
        }
        ...
}

/cpp-app/settings.gradle

rootProject.name = 'cpp-app'

/protocol/build.gradle

plugins {
        id 'maven'
        id 'java'
        id 'com.google.protobuf' version '0.8.8'
}

repositories {
        mavenCentral()
}

description = """"""

sourceCompatibility = 8
targetCompatibility = 8

dependencies {
        compile 'com.google.protobuf:protobuf-java:3.7.0'
        compile 'io.grpc:grpc-stub:1.19.0'
        compile 'io.grpc:grpc-protobuf:1.19.0'
}

sourceSets {
        main {
                proto {
                        srcDir "src/main/proto"
                }
        }
}

protobuf {
        protoc {
                artifact = "com.google.protobuf:protoc:3.7.0"
        }
        plugins {
                grpc_java {
                        artifact = 'io.grpc:protoc-gen-grpc-java:1.19.0'
                }
                grpc_cpp {
                        path = getPluginPath('cpp')
                }
                grpc_python {
                        path = getPluginPath('python')
                }
        }
        generateProtoTasks {
                generatedFilesBaseDir = "${buildDir}/build/generated/src"
                all()*.builtins {
                        java { }
                        cpp { }
                        python { }
                }
                all()*.plugins {
                        grpc_java {
                                outputSubDir = 'java'
                        }
                        grpc_cpp {
                                outputSubDir = 'cpp'
                        }
                        grpc_python {
                                outputSubDir = 'python'
                        }
                }
        }
}

clean {
        delete protobuf.generatedFilesBaseDir
}

// Used to find executables for generating C++ and Java gRPC
static def getPluginPath(name) {
        def path = "which grpc_${name}_plugin".execute()
        path.waitFor()
        path = path.in.text.trim()
        if (!path) {
                println "Failed to locate GRPC plugin for ${name}"
        } else {
                println "Found GRPC plugin for ${name} at ${path}"
        }
        return path
}

/protocol/settings.gradle

rootProject.name = 'protocol'

Then in the project root, I can run gradle assemble, and:

$ gradle assemble

> Configure project :protocol
Found GRPC plugin for cpp at /usr/bin/grpc_cpp_plugin
Found GRPC plugin for python at /usr/bin/grpc_python_plugin

> Task :cpp-app:linkMainExecutable NO-SOURCE
> Task :cpp-app:mainExecutable UP-TO-DATE
> Task :cpp-app:assemble UP-TO-DATE
> Task :protocol:extractIncludeProto
> Task :protocol:extractProto
> Task :protocol:generateProto
> Task :protocol:compileJava
> Task :protocol:processResources
> Task :protocol:classes
> Task :protocol:jar
> Task :protocol:assemble
> Task :java-app:extractIncludeProto
> Task :java-app:extractProto
> Task :java-app:generateProto
> Task :java-app:compileKotlin
> Task :java-app:compileJava
> Task :java-app:processResources
> Task :java-app:classes
> Task :java-app:inspectClassesForKotlinIC
> Task :java-app:jar
> Task :java-app:startScripts
> Task :java-app:distTar
> Task :java-app:distZip
> Task :java-app:assemble

BUILD SUCCESSFUL in 10s
17 actionable tasks: 17 executed
like image 71
Mark K Cowan Avatar answered Nov 07 '22 01:11

Mark K Cowan