sábado, 19 de febrero de 2011

Using native libraries with gradle

I was looking for a way to use native libraries in a java project managed with gradle but I couldn't find any. That why I'm publishing the plugin I created to do so.

We have a multi-platform java project that uses natives JNI libraries to perform some computing intensive tasks. We use maven deploy:deploy-file to package the natives libraries in a jar and upload them to our server. The jar name structure is:
artefactId-x.x.x-platform.jar

The platform tag can be linux, win32 or win64.

So here comes the plugi I created to integrate the native library artifact in the whole gradle project. I'm learning gradle and groovy while doing this so don't expect a very elegant code.
A convention called nativeLib is used to provide the actual native artifact and the libraries are extracted to a directory called build/natives.

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.Task
import java.util.zip.*

class GetNatives implements Plugin {

def String url = "server_url"
def String nativeDirName = 'build/natives/'

/**
* This method extracts the jar provided in the URL and uncompress it in the given destination directory.
*/
def void getNativeJar (String addr, String dstDir) {

URL url = new URL (addr)
URLConnection uc = url.openConnection()

BufferedOutputStream dest = null;
ZipInputStream zis = new ZipInputStream (uc.getInputStream());
ZipEntry entry;
final int BUFFER=2048
while((entry = zis.getNextEntry()) != null) {
String fullName = dstDir + "/" + entry.getName();

// We ignore this directory
if (!(entry.getName().startsWith ('META-INF')))
{

if (entry.isDirectory ()) {

File theFile = new File (fullName)
theFile.mkdir ()
} else {
int count;
byte []data = new byte[BUFFER];
// write the files to the disk
FileOutputStream fos = new FileOutputStream(fullName);
dest = new
BufferedOutputStream(fos, BUFFER);
while ((count = zis.read(data, 0, BUFFER))
!= -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
}
}

zis.close ()


}

def void getNativeLibraries (String theBinString, File nativeDir) {
if (theBinString != 'none') {

nativeDir.mkdirs ()

String platformStr = GetPlatform.getPlatform()

String nativeLib = theBinString
List dependencyList = nativeLib.tokenize(',')
for (entry in dependencyList) {

List theList = entry.tokenize (':')
if ( theList.size() == 3) {
String path = theList [0].replaceAll ("\\.", '/') + '/' + theList[1] + '/' \
+ theList[2] + '/'
String jarName = theList[1] + '-' + theList[2] + '-natives-' \
+ platformStr + '.jar'
getNativeJar ( url + path + jarName, nativeDir.toString())
} else {
println 'Invalid native lib ' + entry
throw new Exception ()
}
}
}
}

def processProject (Project theProject, File nativeDir) {
// Process the project
getNativeLibraries (theProject.convention.plugins.nativeLib.bin, nativeDir)

// Process the children
for (entry in theProject.configurations.default.allDependencies) {

// Avoid the external dependencies
if ( entry.group.tokenize("\\.")[0] == theProject.group.tokenize("\\.")[0]) {
processProject (entry.dependencyProject.project, nativeDir)
}
}

}

def void apply(Project project) {
File nativeDir = project.file (nativeDirName)
project.convention.plugins.nativeLib = new GetNativesPluginConvention ()
project.task ('getNatives') {
outputs.dir nativeDir

doLast {
processProject (project, nativeDir)
}
}
}

}

class GetNativesPluginConvention {
def String bin = 'none'

def nativeLib (Closure closure) {
closure.delegate = this
closure()
}
}


class GetPlatform {
def static String OS_NAME=System.properties ['os.name']
def static String LINUX_NAME='Linux'
def static String OS_ARCH=System.properties ['os.arch']
def static String X86_NAME='x86'

// This method returns the platform we're running according to the system properties
static String getPlatform () {
String platform = "linux"

if (OS_NAME != LINUX_NAME) {
if (OS_ARCH == X86_NAME) {
platform = "win32"
} else {
platform = "win64"
}
}
return platform
}

static boolean isWindows () {
return (OS_NAME != LINUX_NAME)
}

static boolean isLinux () {
return (OS_NAME == LINUX_NAME)
}
}


In order to use this plugin from a build.gradle script the following lines must be added:
apply plugin: 'GetNatives'

nativeLib {
bin = 'org.foo:bar:2.1.1'
}

// Tell the JVM where to find the native libraries
test {
jvmArgs = ['-Djava.library.path=./build/natives']
}


nativeLib {
bin = 'groupId:artifactId:x.x.x'
}


No hay comentarios:

Publicar un comentario