Managing non-java resources from Gradle

There are really complex projects which consist of a lot of heterogeneous parts, modules, batch scripts, binaries...

Let's consider a project containing a module written in C... How to proceed in this case when we want to manage everything by the same way, compile from Gradle and version in a Maven repository?

This article describes an easy way how to achieve this tough DevOps goal.


Building non-java resources with Gradle

For building non-java sources there is a package of plugins for dealing with native binaries, this package is currently under incubating but seems to be pretty usable so far.

Details can be found in the Building native binaries chapter of the official documentation.

There is a support for several languages like C, C++, Assembly, Windows resources, ...

We will focus on C here, as the language differences are not the point of our issue.

C builds with Gradle

Applying the "c" Gradle plugin with a simple setting of source codes located in src/main/c and src/main/c/headers in the project folder:

apply plugin: 'c'

executables {
  main { }
}

sources {
  main {
    c {
      source { 
        srcDir "src/main/c" 
        include "**/*.c" 
      }
      exportedHeaders { 
        srcDir "src/main/c/headers" 
        include "**/*.h" 
      }
    }
  }
}

This script will simply compile the sources in the source path and put the result (.exe file) into build/binaries/mainExecutable folder.

More details can be found in the documentation (mentioned above).

Publish non-java resources into a Maven2 repository

For publishing we need to have an access to a Maven2 repository, for instance the Nexus server from Sonatype.

The goal here is to version the binaries in the repository, and what's more - we would like to make different variants of binaries (for instance each variant for a different platform).

Using the Gradle Maven Publishing plugin is a pretty straightforward way to do this:

apply plugin: 'maven-publish'

group = 'cz.net21.ttulka'
version = '0.1'

task myZip(type: Zip) { 
  destinationDir = file('dist') 
  archiveName 'myC.zip' 
  from 'build/binaries/mainExecutable' 
}

publishing {
  publications {
    myPublicationName(MavenPublication) {
      artifact (myZip) {
        classifier = 'win32'
      }    
    }
  }
}

repositories {
  maven {
    credentials { 
      username 'xxx' 
      password 'xyz' 
    }
    url 'http://localhost:8081/nexus/content/repositories/MyMavenRep1/'
  }
}

Don't forget to set the project name in the settings.gradle configuration file, for instance:

rootProject.name = 'my-project-with-native-binaries'

In the example snippet we have used the Zip Gradle task to archive the binaries into a zip file, then we have used the archive as the input for the Maven publication and finally added the classifier (classifier = 'win32') to annotate this build variant as a Win32 platform-oriented.

Load the binaries as a dependency in an independent project

As far as we have compiled the sources and put the binaries into the repository, we want to deal with them from another project (or a different module).

To distinguish the binary dependencies from the others, we can define a new Gradle configuration:

configurations {
  binaries {}
}

Dependencies are then marked by this configuration flag, don't forget that we used the zip archive and the classifier:

dependencies { 
  binaries 'cz.net21.ttulka:my-project-with-native-binaries:0.1:win32@zip' 
}

That's it, now we can do whatever we want:

// copy the dependencies into a 'deps' folder
task getDeps(type: Copy) { 
  from configurations.binaries 
  into 'deps/' 
}


Hopefully this article gave you a little insight how to deal with different types of resources in one unified way with Gradle, and help you optimize your DevOps processes!