Native: changing the output structure artifacts


Our c++ build process is using a different scheme for the objects/libs/exe produced compare to the one used by gradle. Specifficaly

  • The debug version has the same same as the release version with a ‘d’ appended, ie. food.exe is the debug version while foo.exe is the release version as well as bard.dll is the debug version while bar.dll is the release version
  • All exe (debug and release) are going into the project/bin directory
  • All lib (debug & release) are going into the project/lib directory
  • All dll (debug & release) are going into the project/bin directory

Is there a easy way to change gradle to produce such artifacts?


It might be easier to create a copy task that collects all resulting binaries. To dynamically detect which binaries need to be copied, I would write a plugin, like so (I haven’t tested it):

class BinaryCollectorPlugin extends RuleSource {
    public void createCollectBinariesTask(ModelMap<Task> tasks, ModelMap<NativeBinarySpec> binaries) {
        tasks.create('collectBinaries', Copy) { task ->
            binaries.each { binary ->
                task.dependsOn binary.buildTask
                String buildType =
                if (binary instanceof NativeExecutableBinarySpec)
                    addBinary(task, binary.file, buildType, 'bin')
                else if (binary instanceof SharedLibraryBinarySpec)
                    addBinary(task, binary.sharedLibraryFile, buildType, 'bin')
                else if (binary instanceof StaticLibraryBinarySpec)
                    addBinary(task, binary.staticLibraryFile, buildType, 'lib')
    private void addBinary(Copy task, File binaryFile, String buildType, String destinationDirectory) {
        task.from(binaryFile) {
            into destinationDirectory
            if (buildType == 'debug') {
                rename { originalName ->
                    int lastPeriod = originalName.lastIndexOf('.')
                    if (lastPeriod < 0)
                        return originalName
                    else {
                        String fileName = originalName.substring(0, lastPeriod)
                        String extension = originalName.substring(lastPeriod + 1)
                        return "${fileName}d.$extension"

You can then apply the plugin and add the task to the build task (or whichever other task you prefer):

apply plugin: BinaryCollectorPlugin collectBinaries

Hi Janito

I am trying your proposal but I got this error
A problem occurred configuring root project ‘Foundation’.

There is a problem with model rule BinaryCollectorPlugin#createCollectBinariesTask(ModelMap, ModelMap).
Type-only model reference of type org.gradle.model.ModelMap<org.gradle.nativeplatform.NativeBinarySpec> (parameter 2) is ambiguous as multiple model elements are available for this type:
- binaries (created by: BinaryBasePlugin.Rules#binaries(BinaryContainer))
- components (created by: ComponentBasePlugin.PluginRules#components(ComponentSpecContainer))

How can I fix this?

To fix that issue, we have to tell Gradle how to resolve the ambiguity. The easiest way is to annotate the parameter with the model path of the desired element. In our case, we want the binaries. The method declaration becomes:

public void createCollectBinariesTask(ModelMap<Task> tasks, @Path("binaries") ModelMap<NativeBinarySpec> binaries) {

Hope this helps :slight_smile:

Hi Janito

Thanks for the fix on the ambiguity… Now I got this error

gradle --no-daemon collectBinaries --stacktrace

Caused by: org.gradle.api.InvalidUserDataException: No value has been
specified for property ‘destinationDir’.

A problem was found with the configuration of task ‘:collectBinaries’.

That’s weird. It seems that no binaries were added. Could you please add a debug line in the addBinary method so that we can see if it’s finding the binaries properly?

task.from(BinaryFile) {
    System.err.println("Adding binary $binaryFile to $destinationDirectory");

And also debug line before the if conditions in the collectBinaries method:

String buildType =
System.err.println("Found binary $binary of type ${binary.class}")
System.err.println("Build type is: $buildType")

This way, we can see if the destination directory wasn’t set because it didn’t add any binaries.

I added

                String buildType =
                 destinationDir = new File('foo')

and it works and thank you, it does what I am expecting!!

but if I add

            String buildType =
             destinationDir = project.rootProject.rootDir

it fails with


FAILURE: Build failed with an exception.

  • What went wrong:
    Failed to capture snapshot of output files for task ‘collectBinaries’ property ‘destinationDir’ during up-to-date check.

Failed to create MD5 hash for file ‘C:\Git\poco-gradle\Foundation.gradle\3.2-20160922053809+0000\taskArtifacts\’.

In fact, the latest code as “destinationDir = project.rootProject.rootDir” is the real target…

I found a solution by using --project-cache-dir C:\tmp

Thank for your help. I can know plan to remove CMake over all modules of my project

Awesome! Glad that I was able to be useful :slight_smile: