Luther
Luther

Reputation: 1848

Linking external libraries using NDK, Gradle & CMake in Android Studio

I've come back to Android development after a gap and my old ANT based build chain no longer seem to function (that's a separate issue) with the latest SDK, so I'm trying to do things the new way, which is based around gradle and CMake.

I have a number of pre-built static and dynamic 3rd party libraries that my project needs but I've been unable to link these successfully.
From what I can gather, these need to be specified in the 'CMakeLists.txt' file rather than the 'build.gradle' files but I'm new to both systems, so please correct me if I'm wrong.

Here's what I've tried so far:

${LIBBASE} is the base dir for the 3rd party libraries and seems to be OK but, here's another thing: CMake errors don't seem to appear in android studio! I can sort of work out where it's having problems by running cmake . in the folder that contains the 'CMakeLists.txt' file but I'm not quite sure I'm calling that with the correct parameters: whatever Android Studio does with cmake and that file are a black box to me at this time.

The only messages I can see in android studio are linker errors (it doesn't mention CMake errors about not finding the libraries, which is the cause of these linker errors:)

Build command failed.
Error while executing process E:\prog\Android\cmake\3.6.4111459\bin\cmake.exe with arguments {--build E:\prog\anthracite\gradle\AnthracitePlayerAPI21\app\.externalNativeBuild\cmake\debug\x86_64 --target anthracite-lib}
[1/1] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libanthracite-lib.so
FAILED: cmd.exe /C "cd . && E:\prog\Android\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe  --target=x86_64-none-linux-android --gcc-toolchain=E:/prog/Android/ndk-bundle/toolchains/x86_64-4.9/prebuilt/windows-x86_64 --sysroot=E:/prog/Android/ndk-bundle/sysroot -fPIC -isystem E:/prog/Android/ndk-bundle/sysroot/usr/include/x86_64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -std=c++11 -fexceptions -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a --sysroot E:/prog/Android/ndk-bundle/platforms/android-21/arch-x86_64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libanthracite-lib.so -o ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libanthracite-lib.so @CMakeFiles/anthracite-lib.rsp  && cd ."

E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:237: error: undefined reference to 'btDbvtBroadphase::btDbvtBroadphase(btOverlappingPairCache*)'
E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:240: error: undefined reference to 'btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(btDefaultCollisionConstructionInfo const&)'
E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:242: error: undefined reference to 'btCollisionDispatcher::btCollisionDispatcher(btCollisionConfiguration*)'
E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:243: error: undefined reference to 'btSequentialImpulseConstraintSolver::btSequentialImpulseConstraintSolver()'
E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:246: error: undefined reference to 'btDefaultSoftBodySolver::btDefaultSoftBodySolver()'
E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:247: error: undefined reference to 'btSoftRigidDynamicsWorld::btSoftRigidDynamicsWorld(btDispatcher*, btBroadphaseInterface*, btConstraintSolver*, btCollisionConfiguration*, btSoftBodySolver*)'
E:\prog\anthracite\src/CCmpPhysicsScene3D.cpp:250: error: undefined reference to 'btDiscreteDynamicsWorld::btDiscreteDynamicsWorld(btDispatcher*, btBroadphaseInterface*, btConstraintSolver*, btCollisionConfiguration*)'
E:\prog\anthracite\src/CCmpPhysJointHinge.cpp:117: error: undefined reference to 'btHingeConstraint::btHingeConstraint(btRigidBody&, btVector3 const&, btVector3 const&, bool)'

clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

And running 'cmake .' from the command line gives:

-- Selecting Windows SDK version 10.0.16299.0 to target Windows 10.0.17133.
CMake Warning at CMakeLists.txt:447 (message):
  resolved libraries:


CMake Warning at CMakeLists.txt:448 (message):
  e:/prog/libs/bullet3/build3/Android/obj/local/armeabi-v7a


CMake Warning at CMakeLists.txt:449 (message):
  bullet_lib-NOTFOUND



-- Configuring done
-- Generating done
-- Build files have been written to: E:/prog/anthracite/gradle/AnthracitePlayerAPI21/app

although, as I mentioned above, I'm not sure of the veracity of that output as the calling parameters to 'cmake' are likely to be quite different from inside Android Studio. (e.g it's defaulting to a windows build, so I'm not sure if it'd look for '.lib' libraries rather than '.a' or '.so')

Also, I'm using these lines in my 'CMakeLists.txt' file to report the status of the build:

message(WARNING "resolved libraries:")
message(WARNING ${LIBBASE}bullet3/build3/Android/obj/local/armeabi-v7a)
message(WARNING ${bullet_lib})

Anyway, I'm clearly missing something and I've not been able to find any clear guides regarding this. It seems a really simple and obvious thing to do (link a library) but it seems to be a massive pain. I'd be grateful for any pointers.

Upvotes: 4

Views: 4666

Answers (1)

Alex Cohn
Alex Cohn

Reputation: 57203

You must take care of ABI incompatibility. You are building libanthracite-lib.so for x86_64, so you need the same variant of libBullet.a. If you only need armeabi-v7a, you must specify this in build.gradle, e.g.

android {
  externalNativeBuild {
    cmake {
      path 'CMakeLists.txt'
    }
  }
  defaultConfig {
     ndk {
        abiFilters 'armeabi-v7a'
    }
    externalNativeBuild {
      cmake {
         arguments '-DCMAKE_VERBOSE_MAKEFILE=ON'
      }
    }
  }
}

In your E:\prog\anthracite\gradle\AnthracitePlayerAPI21\app\CMakeLists.txt

add_library(bullet_lib STATIC IMPORTED)
set_target_properties(bullet_lib PROPERTIES IMPORTED_LOCATION
 ${LIBBASE}/bullet3/build3/Android/obj/local/${ANDROID_ABI}/libBullet.a)
target_link_libraries(my_project_name bullet_lib android log EGL GLESv2) 

The order of libraries in target_link_libraries may be important, so keep static libs on the left.

I guess you build libBullet.a with ndk-build. You can create a separate library module (let's call in bullet_module) to your AS Project, even if it has no Java files, and point it to the Android.mk:

apply plugin: 'com.android.library'

android {
  compileSdkVersion 27

  defaultConfig {
    ndk {
      abiFilters 'armeabi-v7a'
    }
    externalNativeBuild {
      ndkBuild {
        targets 'Bullet'
      }
    }
  }
  externalNativeBuild {
    ndkBuild {
      path "${LIBBASE}/bullet3/build3/Android/jni/Android.mk"
    }
  }
}

Now you can change your CMakeLists.txt to look at results of bullet_module build:

set_target_properties(bullet_lib PROPERTIES IMPORTED_LOCATION
  <path/to/bullet_module>/build/intermediates/ndkBuild/debug/obj/local/${ANDROID_ABI}/libBullet.a)

Upvotes: 1

Related Questions