Compile a C++ shared library for tests

Hi folks,

First of all, let me tell you that I’m new to Gradle so I’m probably missing some important concepts.

This is working:

My project contains Java classes in src/main/java that load a shared libname.so library, compiled by the same project from the C++ files in src/main/cpp. The build.gradle.kts file shared below works well: I’m using both cpp-library and java-library plugins. I get the following output files from its execution: build/lib/main/debug/libname.so, build/lib/main/release/libname.so, build/libs/name.jar.

What I would like to achieve:

I have also some test code in src/test/cpp and src/test/java. I would like to compile these C++ files to a shared libname_test.so library, which would be loaded in turn by my Java tests (that use TestNG). In short, I would like to get the following output files too: build/lib/test/debug/libname_test.so, build/lib/test/release/libname_test.so, which would be loaded by a Java test task. I don’t know how to do that. How can I change the layout mentioned in this section?

My build.gradle.kts:

import java.io.*
import org.gradle.internal.jvm.Jvm

plugins {
    `cpp-library`
    `java-library`
}

val nameDir = "../../.."
val nameSrc = "src"

val currentDir: String? by project

val myUsage = Attribute.of("org.gradle.usage", Usage::class.java)

configurations.all {
    afterEvaluate {
        attributes {
            attribute(myUsage, project.objects.named(Usage::class.java, "java-runtime"))
        }
    }
}

val jniImplementation by configurations.creating

configurations.matching {
    it.name.startsWith("cppCompile") ||
        it.name.startsWith("nativeLink") ||
        it.name.startsWith("nativeRuntime")
}.all {
    extendsFrom(jniImplementation)
}

dependencies {
    attributesSchema {
        attribute(myUsage)
    }

    testImplementation("org.testng:testng:6.9.10")
}

tasks.register<Copy>("copyHeaders") {
    from(
        "$keraDir/$nameSrc",
        "$keraDir/${findNameBin()}"
    )
    include("**/*.h")
    into("$buildDir/headers")
}

tasks.withType<CppCompile>().configureEach {
    dependsOn("copyHeaders")
}

/*
tasks.getByName<Test>("test") {
    useTestNG()

    minHeapSize = "1024m"
    maxHeapSize = "1024m"

    dependsOn("linkDebug")
}
*/

val jniHeaderDirectory = layout.buildDirectory.dir("jniHeaders")

tasks.compileJava {
    outputs.dir(jniHeaderDirectory)
    options.compilerArgumentProviders.add(CommandLineArgumentProvider {
        listOf("-h", jniHeaderDirectory.get().asFile.canonicalPath)
    })
}

tasks.compileTestJava {
    outputs.dir(jniHeaderDirectory)
    options.compilerArgumentProviders.add(CommandLineArgumentProvider {
        listOf("-h", jniHeaderDirectory.get().asFile.canonicalPath)
    })
}

library {
    // Private headers are stored in src/main/headers.
    // Public headers are stored build/headers.
    publicHeaders.from("$buildDir/headers")

    binaries.configureEach {
        compileTask.get().dependsOn(tasks.compileJava)

        compileTask.get().compilerArgs.addAll(compileTask.get().toolChain.map {
            if (it is Gcc || it is Clang) listOf("--std=c++11", "-g", "-msse4.2")
            else emptyList()
        })

        compileTask.get().compilerArgs.addAll(jniHeaderDirectory.map {
            listOf("-I", it.asFile.canonicalPath)
        })

        val javaHome = Jvm.current().javaHome.canonicalPath
        compileTask.get().compilerArgs.addAll(compileTask.get().targetPlatform.map {
            listOf("-I", "$javaHome/include") + when {
                it.operatingSystem.isMacOsX -> listOf("-I", "$javaHome/include/darwin")
                it.operatingSystem.isLinux -> listOf("-I", "$javaHome/include/linux")
                else -> emptyList()
            }
        })
    }
}

// Gradle populates library.binaries in afterEvaluate, so we can't access it earlier
afterEvaluate {
    library.binaries.get()
        .filterIsInstance<CppSharedLibrary>()
        .forEach { binary ->
            binary.linkTask.get().linkerArgs.add("-L../../../obj.${getCurrentGitBranch()}")
        }
}

tasks.jar {
    from(library.developmentBinary.flatMap {
        (it as CppSharedLibrary).linkFile
    })
}

Any ideas? Let me know if I should provide more details.

Best regards,
Tom