1. Added support for building the PairHMM vector library into build.xml.

The library is compiled using  makefile and copied into the directory:
build/java/classes/org/broadinstitute/sting/utils/pairhmm/
2. Bundled the library into StingUtils.jar. Unpacked and loaded at
runtime without the need to set java.library.path

Caveats:
Platform independence has probably been thrown out of the window.
Assumptions:
a. make command exists at /usr/bin/make
b. rsync command exists at /usr/bin/rsync
c. icc is in the PATH of the user
This commit is contained in:
Karthik Gururaj 2014-02-07 13:13:59 -08:00
parent 7815c30df8
commit dc44b64ad8
2 changed files with 172 additions and 56 deletions

125
build.xml
View File

@ -64,6 +64,7 @@
<property name="R.private.scripts.dir" value="${private.dir}/R/scripts" />
<property name="R.protected.scripts.dir" value="${protected.dir}/R/scripts" />
<property name="R.public.src.dir" value="${public.dir}/R/src" />
<property name="vector.pairhmm.library.source.dir" value="${public.dir}/c++/VectorPairHMM" />
<!-- Build directories -->
<property name="java.classes" value="${build.dir}/java/classes" />
@ -270,21 +271,21 @@
<mkdir dir="${lib.dir}"/>
<mkdir dir="${ivy.jar.dir}"/>
<!-- Comment out the following lines to build the GATK without a network connection, assuming you have all of the libraries cached already -->
<!-- Comment out the following lines to build the GATK without a network connection, assuming you have all of the libraries cached already -->
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/${ivy.jar.file}"
dest="${ivy.jar.dir}/${ivy.jar.file}"
usetimestamp="true"/>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant"
classpath="${ivy.jar.dir}/${ivy.jar.file}"/>
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/${ivy.jar.file}"
dest="${ivy.jar.dir}/${ivy.jar.file}"
usetimestamp="true"/>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant"
classpath="${ivy.jar.dir}/${ivy.jar.file}"/>
<get src="http://repo1.maven.org/maven2/org/apache/maven/maven-ant-tasks/${maven-ant-tasks.install.version}/${maven-ant-tasks.jar.file}"
dest="${ivy.jar.dir}/${maven-ant-tasks.jar.file}"
usetimestamp="true"/>
<taskdef resource="org/apache/maven/artifact/ant/antlib.xml"
uri="antlib:antlib:org.apache.maven.artifact.ant"
classpath="${ivy.jar.dir}/${maven-ant-tasks.jar.file}"/>
<get src="http://repo1.maven.org/maven2/org/apache/maven/maven-ant-tasks/${maven-ant-tasks.install.version}/${maven-ant-tasks.jar.file}"
dest="${ivy.jar.dir}/${maven-ant-tasks.jar.file}"
usetimestamp="true"/>
<taskdef resource="org/apache/maven/artifact/ant/antlib.xml"
uri="antlib:antlib:org.apache.maven.artifact.ant"
classpath="${ivy.jar.dir}/${maven-ant-tasks.jar.file}"/>
<!-- End network lines -->
@ -601,14 +602,14 @@
<path id="doclet.classpath">
<path refid="external.dependencies" />
<pathelement location="${java.classes}" />
<pathelement location="${clover.jar}" />
<pathelement location="${clover.jar}" />
</path>
<javadoc doclet="org.broadinstitute.sting.utils.help.ResourceBundleExtractorDoclet"
docletpathref="doclet.classpath"
classpathref="external.dependencies"
classpath="${java.classes}"
maxmemory="2g"
maxmemory="2g"
additionalparam="-build-timestamp &quot;${build.timestamp}&quot; -absolute-version ${build.version} -out ${basedir}/${resource.path} -quiet">
<sourcefiles>
<union>
@ -648,9 +649,23 @@
</copy>
</target>
<target name="sting-utils.jar" depends="gatk.compile, init.jar, R.public.tar, R.script.stage">
<target name="build.vector.pairhmm.library">
<exec dir="${vector.pairhmm.library.source.dir}" executable="/usr/bin/make">
</exec>
</target>
<target name="copy.vector.pairhmm.library" depends="build.vector.pairhmm.library">
<exec executable="/usr/bin/rsync">
<arg value="-a"/>
<arg value="${vector.pairhmm.library.source.dir}/libVectorLoglessPairHMM.so"/>
<arg value="${java.classes}/org/broadinstitute/sting/utils/pairhmm/libVectorLoglessPairHMM.so"/>
</exec>
</target>
<target name="sting-utils.jar" depends="gatk.compile, init.jar, R.public.tar, R.script.stage, copy.vector.pairhmm.library">
<jar jarfile="${dist.dir}/StingUtils.jar">
<fileset dir="${java.classes}" includes="**/sting/utils/**/*.class"/>
<fileset dir="${java.classes}" includes="**/sting/utils/**/*.so"/>
<fileset dir="${java.classes}" includes="**/sting/commandline/**/*.class"/>
<fileset dir="${java.classes}" includes="**/sting/pipeline/**/*.class"/>
<fileset dir="${java.classes}" includes="**/sting/tools/**/*.class"/>
@ -682,9 +697,9 @@
<include name="org/broadinstitute/sting/gatk/walkers/na12878kb/core/**/*.class"/>
<include name="net/sf/picard/reference/FastaSequenceFile.class"/>
</fileset>
<fileset dir="${java.private.source.dir}">
<fileset dir="${java.private.source.dir}">
<include name="org/broadinstitute/sting/gatk/walkers/na12878kb/core/resources/**/*"/>
</fileset>
</fileset>
</jar>
</target>
@ -693,7 +708,7 @@
<path refid="gatk.resources"/>
<fileset dir="${java.contracts.dir}" />
<fileset dir="${java.classes}">
<include name="${resource.file}" />
<include name="${resource.file}" />
<include name="**/sting/gatk/**/*.class" />
<include name="**/sting/alignment/**/*.class"/>
</fileset>
@ -815,7 +830,7 @@
docletpathref="doclet.classpath"
classpathref="external.dependencies"
classpath="${java.classes}"
maxmemory="2g"
maxmemory="2g"
additionalparam="${gatkdocs.include.hidden.arg} -private -build-timestamp &quot;${build.timestamp}&quot; -absolute-version ${build.version} -quiet"> <!-- -test to only do DocumentationTest walker -->
<sourcefiles>
<fileset refid="java.source.files"/>
@ -974,10 +989,10 @@
<!-- Build out a classpath -->
<pathconvert property="required.picard.jars" pathsep=":">
<fileset dir="${basedir}">
<include name="${staging.dir}" />
<include name="${lib.dir}/picard-*.*.*.jar" />
<include name="${lib.dir}/sam-*.jar" />
</fileset>
<include name="${staging.dir}" />
<include name="${lib.dir}/picard-*.*.*.jar" />
<include name="${lib.dir}/sam-*.jar" />
</fileset>
</pathconvert>
<echo message="required.picard.jars=${required.picard.jars}" />
@ -990,7 +1005,7 @@
<mkdir dir="${package.output.dir}" />
<xslt in="${package.xml.dir}/PicardPrivate.xml" out="${package.output.dir}/BuildPicardPrivate.xml" style="${package.xml.dir}/CreatePackager.xsl" />
<ant antfile="${package.output.dir}/BuildPicardPrivate.xml">
<property name="additional.jars" value="${required.picard.jars}" />
<property name="additional.jars" value="${required.picard.jars}" />
</ant>
</target>
@ -1149,31 +1164,31 @@
<target name="clover.report">
<clover-report coverageCacheSize="nocache">
<current outfile="clover_html" title="GATK clover report" showUniqueCoverage="false" numThreads="4">
<format type="html" filter="catch,static,property"/>
<fileset dir="public">
<patternset id="clover.excludes">
<exclude name="**/*UnitTest.java"/>
<exclude name="**/*TestProvider*.java"/>
<exclude name="**/*PerformanceTest.java"/>
<exclude name="**/*Benchmark.java"/>
<exclude name="**/*LargeScaleTest.java"/>
<exclude name="**/*IntegrationTest.java"/>
<exclude name="**/jna/**/*.java"/>
<exclude name="**/queue/extensions/**/*.java"/>
<exclude name="**/sting/utils/help/*.java"/>
<exclude name="**/sting/tools/*.java"/>
<exclude name="**/datasources/reads/utilities/*.java"/>
<exclude name="**/sting/alignment/**/*.java"/>
<exclude name="**/examples/**/*.java"/>
</patternset>
</fileset>
<fileset dir="private">
<patternset refid="clover.excludes" />
</fileset>
<fileset dir="protected">
<patternset refid="clover.excludes" />
</fileset>
</current>
<format type="html" filter="catch,static,property"/>
<fileset dir="public">
<patternset id="clover.excludes">
<exclude name="**/*UnitTest.java"/>
<exclude name="**/*TestProvider*.java"/>
<exclude name="**/*PerformanceTest.java"/>
<exclude name="**/*Benchmark.java"/>
<exclude name="**/*LargeScaleTest.java"/>
<exclude name="**/*IntegrationTest.java"/>
<exclude name="**/jna/**/*.java"/>
<exclude name="**/queue/extensions/**/*.java"/>
<exclude name="**/sting/utils/help/*.java"/>
<exclude name="**/sting/tools/*.java"/>
<exclude name="**/datasources/reads/utilities/*.java"/>
<exclude name="**/sting/alignment/**/*.java"/>
<exclude name="**/examples/**/*.java"/>
</patternset>
</fileset>
<fileset dir="private">
<patternset refid="clover.excludes" />
</fileset>
<fileset dir="protected">
<patternset refid="clover.excludes" />
</fileset>
</current>
</clover-report>
</target>
@ -1220,7 +1235,7 @@
<target name="test.scala.compile" depends="test.java.compile,scala.compile" if="include.scala">
<echo message="Scala: Compiling test cases!"/>
<scalac fork="true" jvmargs="-Xmx512m" destdir="${scala.test.classes}" deprecation="yes" unchecked="yes" addparams="-feature">
<src refid="scala.test.source.path" />
<src refid="scala.test.source.path" />
<classpath>
<path refid="build.results"/>
<pathelement location="${java.test.classes}"/>
@ -1266,13 +1281,13 @@
<echo message="Sting: Running @{testtype} test cases!"/>
<echo message="Test Memory : ${test.maxmemory}" />
<!-- no test is allowed to run for more than 10 hours -->
<!-- no test is allowed to run for more than 10 hours -->
<taskdef resource="testngtasks" classpath="${testng.jar}"/>
<testng outputDir="@{outputdir}"
classpathref="${testng.classpath}"
haltOnFailure="false" failureProperty="test.failure"
verbose="2"
timeout="36000000"
timeout="36000000"
workingDir="${basedir}"
useDefaultListeners="false"
listeners="org.testng.reporters.FailedReporter,org.testng.reporters.JUnitXMLReporter,org.broadinstitute.sting.TestNGTestTransformer,org.broadinstitute.sting.StingTextReporter,org.uncommons.reportng.HTMLReporter">
@ -1308,7 +1323,7 @@
<report format="noframes" todir="@{outputdir}"/>
</junitreport>
<!-- copy the report to our private_html directory for easy viewing in a broswer -->
<!-- copy the report to our private_html directory for easy viewing in a broswer -->
<mkdir dir="${iwww.report.dir}/@{testtype}"/>
<copy todir="${iwww.report.dir}/@{testtype}" verbose="false">
<fileset dir="@{outputdir}"/>
@ -1442,7 +1457,7 @@
<!-- Fast test target that cuts major corners for speed. Requires that a full build has been done first. Java-only, single test class only -->
<!-- Usage: ant fasttest -Dsingle=TestClass -->
<target name="fasttest" depends="init.javaonly,init" description="Quickly run a single test">
<property name="test.maxmemory" value="${test.default.maxmemory}"/>
<property name="test.maxmemory" value="${test.default.maxmemory}"/>
<condition property="not.clean">
<and>
<available file="${build.dir}" />

View File

@ -59,6 +59,15 @@ import java.util.List;
import java.util.Map;
import java.util.HashMap;
//For loading library from jar
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created with IntelliJ IDEA.
@ -111,7 +120,28 @@ public class VectorLoglessPairHMM extends JNILoglessPairHMM {
synchronized(isVectorLoglessPairHMMLibraryLoaded) {
//Load the library and initialize the FieldIDs
if(!isVectorLoglessPairHMMLibraryLoaded) {
System.loadLibrary("VectorLoglessPairHMM");
try
{
//Try loading from Java's library path first
//Useful if someone builds his/her own library and wants to override the bundled
//implementation without modifying the Java code
System.loadLibrary("VectorLoglessPairHMM");
}
catch(UnsatisfiedLinkError ule)
{
//Could not load from Java's library path - try unpacking from jar
try
{
logger.info("libVectorLoglessPairHMM not found in JVM library path - trying to unpack from StingUtils.jar");
loadLibraryFromJar("/org/broadinstitute/sting/utils/pairhmm/libVectorLoglessPairHMM.so");
}
catch(IOException ioe)
{
//Throw the UnsatisfiedLinkError to make it clear to the user what failed
throw ule;
}
}
isVectorLoglessPairHMMLibraryLoaded = true;
jniInitializeClassFieldsAndMachineMask(JNIReadDataHolderClass.class, JNIHaplotypeDataHolderClass.class, enableAll); //need to do this only once
}
@ -229,4 +259,75 @@ public class VectorLoglessPairHMM extends JNILoglessPairHMM {
super.close();
jniClose();
}
//Copied from http://frommyplayground.com/how-to-load-native-jni-library-from-jar
/**
* Loads library from current JAR archive
*
* The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after exiting.
* Method uses String as filename because the pathname is "abstract", not system-dependent.
*
* @param filename The filename inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext
* @throws IOException If temporary file creation or read/write operation fails
* @throws IllegalArgumentException If source file (param path) does not exist
* @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters (restriction of {@see File#createTempFile(java.lang.String, java.lang.String)}).
*/
public static void loadLibraryFromJar(String path) throws IOException {
if (!path.startsWith("/")) {
throw new IllegalArgumentException("The path to be absolute (start with '/').");
}
// Obtain filename from path
String[] parts = path.split("/");
String filename = (parts.length > 1) ? parts[parts.length - 1] : null;
// Split filename to prexif and suffix (extension)
String prefix = "";
String suffix = null;
if (filename != null) {
parts = filename.split("\\.", 2);
prefix = parts[0];
suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null; // Thanks, davs! :-)
}
// Check if the filename is okay
if (filename == null || prefix.length() < 3) {
throw new IllegalArgumentException("The filename has to be at least 3 characters long.");
}
// Prepare temporary file
File temp = File.createTempFile(prefix, suffix);
//System.out.println("Temp lib file "+temp.getAbsolutePath());
temp.deleteOnExit();
if (!temp.exists()) {
throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist.");
}
// Prepare buffer for data copying
byte[] buffer = new byte[1024];
int readBytes;
// Open and check input stream
InputStream is = VectorLoglessPairHMM.class.getResourceAsStream(path);
if (is == null) {
throw new FileNotFoundException("File " + path + " was not found inside JAR.");
}
// Open output stream and copy data between source file in JAR and the temporary file
OutputStream os = new FileOutputStream(temp);
try {
while ((readBytes = is.read(buffer)) != -1) {
os.write(buffer, 0, readBytes);
}
} finally {
// If read/write fails, close streams safely before throwing an exception
os.close();
is.close();
}
// Finally, load the library
System.load(temp.getAbsolutePath());
}
}