Recommendations for Java/JNI/C++/Cross Compilation


(Fredric Silberberg) #1

As my post title implies, I have a rather complex set of dependencies in a project, and I’m trying to figure out the best way to make all of this work with Gradle. Here’s a basic description of the dependency graph for this project (all cross-compiled elements are cross-compiled for arm with a custom toolchain):

Support Library (C++, cross-compiled) -> User-facing C++ Library (cross-compiled) -> Distribution Zip
                                      -> JNI (C++, cross-compiled) -> User-facing Java Library (needs to have the JNI and support library in the jar)

Currently, I’m working with Gradle version 2.8, as the tricks that I’m using to get the binaries included in the jars/zips are compilation errors in 2.9 and up. This is a simplified version of our buildscripts, that really just shows the interesting parts:

apply plugin: 'cpp'
apply plugin: 'java'

model {
    toolChains {
        target('arm') {
            def compilerPrefix = 'arm-frc-linux-gnueabi-'
            cppCompiler.executable = compilerPrefix + cppCompiler.executable
            linker.executable = compilerPrefix + linker.executable
            assembler.executable = compilerPrefix + assembler.executable
            staticLibArchiver.executable = compilerPrefix + staticLibArchiver.executable
        }
    }
    components {
        jniComponent(NativeLibrarySpec) {
            targetPlatform 'arm'
            sources {
                cpp {
                    source {
                        srcDirs = ["src/jni"]
                        includes = ["**/*.cpp"]
                    }
                }
            }
        }
    }
}

jar {
    // Can't access binaries anymore, compilation error
    binaries.withType(SharedLibraryBinarySpec) {
        from(file(it.sharedLibraryFile)) {
            into 'linux-arm'
        }
    }
}

// This just seems kludgy. There has to be a way to get this dependency 
// without depending on manually-specified strings, right?
tasks.whenTaskAdded { task ->
    if (task.name == 'jniComponentSharedLibrary') {
        jar.dependsOn task
    }
}

I’ve left a couple of comments in there about the current issues I’m having with this setup. First, we can no longer access the binaries element, and I’m not sure how to access the shared library to add to the zip and/or jar anymore. Second, in order to get the dependencies ordered correctly, I have to make sure that the zip/jar tasks depend on the binary compilation tasks, but the only way I seem to be able to add this dependency (because the tasks are only added to the build later in compilation) is by adding a task watcher. This seems incredibly kludgy to me, and it feels like there should be a better way that doesn’t involve depending on task names never changing. Does anyone have any recommendations on how to address these issues?