13

I'm starting a simple java test project using sqlite4java and building using java.

I can get the core sqlite4java library downloaded easily, but I'm not sure what the best (any!) way to get gradle to download the native libraries and put them in the right place.

This is my build.gradle file:

apply plugin: 'java'

/* We use Java 1.7 */
sourceCompatibility = 1.7
targetCompatibility = 1.7
version = '1.0'

repositories {
    mavenCentral()
}

sourceSets {
    main {
        java.srcDir 'src'
        output.classesDir = 'build/main'
    }
    test {
        java.srcDir 'test'
        output.classesDir = 'build/test'
    }
}


dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile "com.almworks.sqlite4java:sqlite4java:1.0.392"
    compile "com.almworks.sqlite4java:libsqlite4java-osx:1.0.392"
}

But when I run a simple test I get:

TestTest > testSqlite4Basic FAILED
    com.almworks.sqlite4java.SQLiteException at TestTest.java:15
        Caused by: java.lang.UnsatisfiedLinkError at TestTest.java:15

(I'm building from within IntelliJ, but am using the gradle build options - so I don't think it's ItelliJ stuffing up the class path when running the tests...)

I'm pretty sure the first time I tried to build I got some message about being unable to unpack the libsqlite4java-osx ZIP file, (not surprisingly as maven central says its a dylib file).

What do I need to do to make gradle do the right thing?

ASIDE: I'm able to get the code to run by manually copying the downloaded .dylib from the maven cache into somewhere listed in my java.library.path (Think I used $HOME/Library/Java/Extensions on my mac). But this seems contrary to the whole point of packaging all setup and dependencies in the .gradle file, and wont let me distribute anything easily later.

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • Looks like there is something that almost does this for android gradle - https://github.com/nhachicha/android-native-dependencies but this doesn't seem to support OS-X. And I'm not sure if there's other issues in using this outside the android version of gradle. – Michael Anderson Sep 04 '15 at 07:23
  • Sorry I can't solve the problem immediately, but I did find this that indicates you're not alone and this is not sqlite specific https://discuss.gradle.org/t/how-can-i-add-a-dylib-to-a-project-dependenies/3115 – J Richard Snape Sep 13 '15 at 07:40
  • http://stackoverflow.com/questions/29437888/using-gradle-with-native-dependencies also looks relevant – J Richard Snape Sep 13 '15 at 07:42

4 Answers4

6

I suppose, you need to use additional gradle plugin to handle native libraries or make your own specific tasks, to upload and put native libs in right place, in order to they could be found and linked.

At the moment I know only about one such a plugin, hope it can solve your problem https://github.com/cjstehno/gradle-natives

Edit: The problem with plugin in your case is the fact, that your dependecny "com.almworks.sqlite4java:libsqlite4java-osx:1.0.392" is native lib by itself, not a jar with included native lib as I supposed. So, in that case, you can simply add this dependency in dependencies par of build script, as it'a already done, and then create a custom copy task, to put it in any place you need. Tried to do it with gradle 2.6 on Win7, look like:

task copyNtiveDeps(type: Copy) {
  from (configurations.compile+configurations.testCompile) {
    include "libsqlite4java-osx-1.0.392.dylib"
  }
  into "c:\\tmp"
}

In your case, you just need to set "into" property to some path from java.library.path. And the second, you can make this task runs automaticaly with gradle task properties dependsOn and mustRunAfter.

Stanislav
  • 27,441
  • 9
  • 87
  • 82
  • It looks like that plugin doesn't actually pull the libraries down from maven, it requires them to already be installed and in your class path. Also you need to download and build the plugin and put it in a local maven cache - seems like a lot of work for such a simple task. – Michael Anderson Sep 14 '15 at 00:43
  • I found the reason, why plugin is not a decision for you, so I made some changes to my answer. You can simply copy any maven dependency manually to any path you need. – Stanislav Sep 14 '15 at 09:03
  • As for plugin itself. It doesn't pull any dependecies, it should be done in usual gradle way, it just extracts native libs from those dependencies. And there is no need to make any additional work to make this plugin wotk inside a gradle, just to add a buildscript dependency and the apply it – Stanislav Sep 14 '15 at 09:18
  • Your answer got me to the solution I needed in the end (added as additional answer...) but I had the previous +100 bounty canceled before you amended your answer... so +200 coming your way in 24 hours. – Michael Anderson Sep 15 '15 at 07:52
  • Glad, that you found a solution and thanks in advance – Stanislav Sep 15 '15 at 08:07
6

Here's a complete answer based on Stanislav's comments.

apply plugin: 'java'

/* We use Java 1.8 */
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.0'

repositories { mavenCentral() }

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile "com.almworks.sqlite4java:sqlite4java:1.0.392"
    compile "com.almworks.sqlite4java:libsqlite4java-osx:1.0.392"
}

sourceSets {
    main {
        java.srcDir 'src'
        output.classesDir = 'build/main'
    }
    test {
        java.srcDir 'test'
        output.classesDir = 'build/test'
    }
}

/* Copy the native files */
task copyNativeDeps(type: Copy) {
    from (configurations.compile+configurations.testCompile) {
        include "*.dylib"
    }
    into 'build/libs'
}

/* Make sure we setup the tests to actually copy 
 * the native files and set the paths correctly. */
test {
    dependsOn copyNativeDeps
    systemProperty "java.library.path", 'build/libs'
}

And example test source to run for it:

import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteStatement;
import org.junit.Test;

import java.io.File;

public class SqliteTest {

    @Test public void aTest() throws Exception {
        SQLiteConnection db = new SQLiteConnection(new File("/tmp/database"));
        db.open(true);  

        SQLiteStatement st = db.prepare("SELECT name FROM dummy");
        try {
            while(st.step()) {
                System.err.printf("name = %s\n", st.columnString(1));
            }
        } finally {
            st.dispose();
        }
    }
}
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
2

For compiling your own sqlite3 libraries, you can check github android-sqlite3

For the current Android Studio (3.3.1), you can simply add the .so files inside app/build.gradle like below:

android {
    ...

    sourceSets {
        main {
            jniLibs.srcDirs += ['<your-path>/your-libs']
        }
    }

    ...
}

Explanation

jniLibs.srcDirs is the gradle path pointing at jni libraries, operator += denotes the additional jni libraries besides the default ones inside app/src/main/jniLibs as below:

app/
├──libs/
|  └── *.jar           <-- java libs
├──src/
   └── main/
       ├── AndroidManifest.xml
       ├── java/
       └── jniLibs/    <-- default directory for jni libs, i.e. <ANDROID_ABI>/**.so

And please note that the jni libraries have to be organized according to android ABI, i.e.

your-libs/
├── arm64-v8a/                       <-- ARM 64bit
│   └── lib-your-abc.so
├── armeabi-v7a/                     <-- ARM 32bit
│   └── lib-your-abc.so
├── x86_64/                          <-- Intel 64bit
│   └── lib-your-abc.so
└── x86/                             <-- Intel 32bit
    └── lib-your-abc.so

shizhen
  • 12,251
  • 9
  • 52
  • 88
0

I used this method to setup sqlite4java in anroid studio 3.1.4.

First download the library from this link: https://bitbucket.org/almworks/sqlite4java

The download will contain a zip file containing a folder named android and other .jar , .so and .dll files.

Copy the three folders in android i.e 'armeabi', 'armeabi-v7a' and 'x86' into a folder named 'jniLibs'.

Copy the above folder i.e 'jniLibs' into yourproject/app/src/main/ folder.

Then implement the gradle dependencies for java4sqlite:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "your.project.name"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}




dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
    implementation "com.almworks.sqlite4java:sqlite4java:1.0.392"
    implementation "com.almworks.sqlite4java:libsqlite4java-osx:1.0.392"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'


}
Muhammad Ali
  • 683
  • 4
  • 9
  • See my answer https://stackoverflow.com/a/54977264/8034839 for how to compile your own sqlite3 libraries. – shizhen Mar 04 '19 at 05:47
  • can you please explain how did you add the path in build.gradle in the following code: jniLibs.srcDirs += ['/your-libs'] will this code add the libraries automatically? or I would have to still copy the folders. – Muhammad Ali Mar 04 '19 at 11:40
  • Yes, it will point to your libs directory. Check my answer in details – shizhen Mar 04 '19 at 12:38