diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed83050 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.gradle +.idea +build +src/main/c/bwa +src/main/c/*.o +src/main/c/*.dylib +src/main/c/*.so +*.swp diff --git a/README.md b/README.md index d4a8d21..f98205b 100644 --- a/README.md +++ b/README.md @@ -4,35 +4,34 @@ JNI code for bwa mem. This project builds dynamic libraries with a JNI API. It allows Java code to call Heng Li's bwa mem aligner. -The makefile in src/main/c will build an appropriate library for Mac OSX or x86_64 Linux. -Pre-compiled dynamic libraries for these OS's exist in src/main/resources. +To build you'll need gmake, git, gcc, and Java 8. -To deploy into maven central: -Find a Mac. - Clone this repository. - Go into ```src/main/c```. - Type ```make``` (you'll need gmake, git, and gcc). - Copy ```libbwa.Darwin.dylib``` to ```src/main/resources```. - Type ```make clean```. - Go back up to the repo root, and type ```gradle test``` to run the Java unit tests. -Find a Linux machine. - Clone this repository. - Go into ```src/main/c```. - Type ```make``` (you'll need gmake, git, and gcc). - Copy ```libbwa.Linux.so``` to ```src/main/resources```. - Type ```make clean```. - Go back up to the repo root, and type ```gradle test``` to run the Java unit tests. - Copy ```src/main/resources/libbwa.Linux.so``` to ```src/main/resources``` ON THE MAC. - (Yes, you have to copy it to the other machine.) -Go back to the Mac. - Type ```gradle deploy``` (you'll need gradle). +To build and install a snapshot locally: +``` +./gradlew install +``` -To use this JNI binding on some architecture for which we don't provide a binary: - Clone the repo. +This will work for testing but will only include a native library for your system. + +To upload a snapshot from a Broad Institute OSX machine with both OSX and Linux binaries: +``` +commit your changes and push your branch to github +scripts/build_both_dylib_and_so.sh +./gradlew uploadArchives printVersion +``` + +To upload to maven central +``` +commit your changes and push your branch to github +git tag -a -s +scripts/build_both_dylib_and_so.sh +./gradlew uploadArchive -Drelease=true +``` + +To use this JNI binding on another architecture for which we don't provide a binary: Go into ```src/main/c```. Modify the Makefile to produce a library name appropriate to your system. Type ```make``` (you'll need gmake, git, and gcc). Move the library you built somewhere permanent on your machine. Use ```-DLIBBWA_PATH=``` when you run GATK (or other Java program). - diff --git a/build.gradle b/build.gradle index 7774c2a..a1bb7e8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,15 @@ -apply plugin: 'java' +buildscript { + repositories { + jcenter() + } +} + +plugins { + id 'java' + id 'maven' + id 'signing' + id 'com.palantir.git-version' version '0.5.1' //version helper +} repositories { mavenCentral() @@ -8,6 +19,144 @@ dependencies { testCompile 'org.testng:testng:6.9.6' } +final isRelease = Boolean.getBoolean("release") +version = (isRelease ? gitVersion() : gitVersion() + "-SNAPSHOT").replaceAll(".dirty", "") +group = "org.broadinstitute" +String cpath = "src/main/c" +String libname = "libbwa" + +task buildBwaLib(type: Exec){ + workingDir "$cpath" + outputs.files "$cpath/libbwa*" + outputs.dir "$cpath/bwa" + commandLine "make" + String home = System.properties."java.home" + //strip the trailing jre + String corrected = home.endsWith("jre") ? home.substring(0, home.length() - 4) : home + environment JAVA_HOME : corrected + doFirst { println "using $home -> $corrected as JAVA_HOME" } +} + +clean { + delete "$cpath/bwa" + delete "$cpath/$libname*" + delete fileTree("$cpath") {include "$libname*", "*.o"} +} + +processResources { + dependsOn buildBwaLib + from cpath + include "$libname*" +} + test { useTestNG() + testLogging { + testLogging { + events "skipped", "failed" + exceptionFormat = "full" + } + afterSuite { desc, result -> + if (!desc.parent) { // will match the outermost suite + println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" + } + } + } +} + +javadoc { + options.addStringOption('Xdoclint:none', '-quiet') +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from 'build/docs/javadoc' +} + +task sourcesJar(type: Jar) { + from sourceSets.main.allSource + classifier = 'sources' } + +/** + *This specifies what artifacts will be built and uploaded when performing a maven upload. + */ +artifacts { + archives jar + archives javadocJar + archives sourcesJar +} + +/** + * Sign non-snapshot releases with our secret key. This should never need to be invoked directly. + */ +signing { + required { isRelease && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives +} + +def assertLibExists(lib){ + if ( ! file(lib).exists()){ + throw new GradleException("Could not perform a maven release because $lib is missing. You must include both OSX and Linux binaries to release. " + + "You can run scripts/build_both_dylib_and_so.sh to build both if you are on a Broad Institute connected mac.") + } +} + +/** + * Upload a release to sonatype. You must be an authorized uploader and have your sonatype + * username and password information in your gradle properties file. See the readme for more info. + * + * For releasing to your local maven repo, use gradle install + */ +uploadArchives { + doFirst { + println "Attempting to upload version:$version" + if (isRelease){ + assertLibExists("$cpath/${libname}.Linux.so") + assertLibExists("$cpath/${libname}.Darwin.dylib") + } + } + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: project.findProperty("sonatypeUsername"), password: project.findProperty("sonatypePassword")) + } + + snapshotRepository(url: "https://artifactory.broadinstitute.org/artifactory/libs-snapshot-local/") { + authentication(userName: System.env.ARTIFACTORY_USERNAME, password: System.env.ARTIFACTORY_PASSWORD) + } + + pom.project { + name 'gatk-bwamem-jni' + packaging 'jar' + description 'java bindings for the bwa-mem assembler' + url 'http://github.com/broadinstitute/gatk-bwamem-jni' + + scm { + url 'scm:git@github.com:broadinstitute/gatk-bwamem-jni.git' + connection 'scm:git@github.com:broadinstitute/gatk-bwamem-jni.git' + developerConnection 'scm:git@github.com:broadinstitute/gatk-bwamem-jni.git' + } + + developers { + developer { + id = "gatkdev" + name = "GATK Development Team" + email = "gatk-dev-public@broadinstitute.org" + } + } + + licenses { + license { + name 'BSD 3-Clause' + url 'https://github.com/broadinstitute/gatk-bwamem-jni/blob/master/LICENSE.TXT' + distribution 'repo' + } + } + } + } + } +} + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..6ffa237 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..22b4935 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Feb 16 17:46:58 EST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9aa616c --- /dev/null +++ b/gradlew @@ -0,0 +1,169 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/scripts/build_both_dylib_and_so.sh b/scripts/build_both_dylib_and_so.sh new file mode 100755 index 0000000..4cf65af --- /dev/null +++ b/scripts/build_both_dylib_and_so.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +#Upload a snapshot build of the current HEAD with both .dylib and .so +#This can only be run from within Broad + +#set -v +set -e + +SERVER=gsa6.broadinstitute.org + +LIB_PATH="src/main/c" + +echo "building local files" +./gradlew clean build + +echo "building remote files" +vectorLib=$( ssh $SERVER 'bash -s' < scripts/build_native_lib_in_clean_repo.sh $( git rev-parse HEAD) gatk-bwamem-jni libbwa $LIB_PATH ) + +echo "result is at $vectorLib" + +echo "copying from ${SERVER}:${vectorLib} to $LIB_PATH" +scp ${SERVER}:${vectorLib} $LIB_PATH + +exit 0 diff --git a/scripts/build_native_lib_in_clean_repo.sh b/scripts/build_native_lib_in_clean_repo.sh new file mode 100755 index 0000000..ec5ca85 --- /dev/null +++ b/scripts/build_native_lib_in_clean_repo.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +#Helper script to be called from build_both_dylib_and_dylib.sh +# +#checkout and clone a new copy of gatk in a tmpdir +#build the .so file and return its location +#this can only be run on broad machines +# +# usage build_native_lib_in_clean_repo.sh + +set -v + +COMMIT=$1 +PROJECT=$2 +LIB_NAME=$3 +LIB_PATH=$4 + + +source /broad/tools/scripts/useuse +reuse Git-2.11 + +set -e + +export TMPDIR="/broad/hptmp" + +echoerr() { echo "$@" 1>&2; } + +PROJECT_TMP_DIR=`mktemp --tmpdir -d 2>/dev/null || mktemp -d -t 'mytmpdir'` + +echoerr "Moving to $PROJECT_TMP_DIR" +cd "$PROJECT_TMP_DIR" + +echoerr "cloning broadinstitute/${PROJECT}" +GIT_LFS_SKIP_SMUDGE=1 git clone git@github.com:broadinstitute/${PROJECT}.git 1>2 + +echoerr "Moving to $PROJECT" +cd $PROJECT + +echoerr "Checking out ${commit}" +GIT_LFS_SKIP_SMUDGE=1 git checkout -f "$COMMIT" 1>2 + +echoerr "performing gradle build" +./gradlew build 1>2 --no-daemon + +LIB=$(pwd)/$(find $LIB_PATH -name "${LIB_NAME}*" ! -name "*.a" ) +echoerr "created $LIB" + +echo $LIB +exit 0 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ae96943 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "gatk-bwamem-jni"