在Android Studio中使用NDK、Gradle和CMake链接外部库

3

我经过一段时间后回到了Android开发,但我的旧的基于ANT的构建链似乎无法与最新的SDK一起使用(这是一个单独的问题),因此我尝试着采用新的方式进行开发,这种方式基于gradle和CMake。

我的项目需要许多预构建的静态和动态第三方库,但我一直没有成功地将它们链接起来。
据我所知,这些库需要在“'CMakeLists.txt”文件中指定而不是“build.gradle”文件中指定,但我对这两个系统都很陌生,如果我有错误,请纠正我。

以下是我迄今为止尝试过的内容:

  • first, I try to resolve the location of bullet. I've tried variants where I omit the 'lib' the '.a' and the 'armeabi-v7a' (hoping it'd pick up the right CPU variant) but none have worked

     find_library(bullet_lib libBullet.a HINTS ${LIBBASE}bullet3/build3/Android/obj/local/armeabi-v7a)
    

    I'm not sure how this differs from the libraries included in target_link_libraries

    add_library(bullet_lib STATIC IMPORTED)
    

    this finds the built-in libraries fine but I get linker errors for 'bullet' and other 3rd party libs

    target_link_libraries(my_project_name android log EGL GLESv2 bullet_lib) 
    
${LIBBASE}是第三方库的基础目录,看起来似乎没问题,但是还有一个问题:CMake错误似乎不会在Android Studio中显示!我可以通过在包含“CMakeLists.txt”文件的文件夹中运行cmake .来大致确定它遇到了什么问题,但我不确定是否使用了正确的参数:无论Android Studio如何处理cmake和该文件,对我来说都是一个黑匣子。
我在Android Studio中唯一能看到的消息是链接器错误(它没有提到找不到库的CMake错误,这就是这些链接器错误的原因)。
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.

在命令行中运行“cmake。”会得到以下结果:

-- 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

虽然我上面提到过,但我不确定'cmake'的调用参数是否与Android Studio内部有所不同,因此我对输出结果的真实性持怀疑态度。(例如,它默认为Windows构建,因此我不确定它是否会查找'.lib'库而不是'.a'或'.so'库)

此外,我在我的'CMakeLists.txt'文件中使用以下几行来报告构建状态:

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

无论如何,显然我错过了什么,而且我没有找到任何关于这方面的明确指南。这似乎是一件非常简单和明显的事情(链接库),但它似乎是一个巨大的痛苦。 如果有任何指导意见,我将不胜感激。


我在问题帖子中没有看到任何错误消息。我有遗漏什么吗? - Tsyvarev
不好意思,我没有添加它们。它们是来自clang的链接器错误:它并没有说无法从Android Studio内部找到库(正如我所说,Android Studio似乎不报告CMake错误,这并不是很有帮助)。我将在我的帖子中更新链接器错误信息。 - Luther
2
如果您选择添加预构建库,可以参考以下示例 https://github.com/googlesamples/android-ndk/tree/master/hello-libs:它展示了如何使用CMake链接(外部)预构建库。最终选择哪种方法取决于您想要采用的方式,您可能会走向ndk-build道路来跟踪您的代码等。 - Gerry
2
add_library(bullet_lib STATIC IMPORTED) 后,您需要使用 set_target_properties(lib PROPERTIES IMPORTED_LOCATION 添加库文件的位置,并使目标包含头文件。 - Vygintas B
1
你可能会发现这个有用:通过Gradle和Android NDK强制CMake进入详细模式 - Alex Cohn
1个回答

1

您必须注意ABI不兼容性。您正在为 x86_64 构建 libanthracite-lib.so,因此您需要相同变体的 libBullet.a。如果您只需要 armeabi-v7a,则必须在 build.gradle 中指定,例如:

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

在你的 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) 

target_link_libraries中的库的顺序可能很重要,因此请将静态库放在左边。
我猜你是使用ndk-build构建了libBullet.a。即使没有Java文件,您也可以为AS项目创建一个单独的库模块(我们称之为bullet_module),并将其指向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"
    }
  }
}

现在你可以修改你的 CMakeLists.txt 来查看 bullet_module 构建的结果:
set_target_properties(bullet_lib PROPERTIES IMPORTED_LOCATION
  <path/to/bullet_module>/build/intermediates/ndkBuild/debug/obj/local/${ANDROID_ABI}/libBullet.a)

嗨,Alex, 按照你在另一个论坛上的建议,我一直在使用我的旧android.mk构建文件,这些文件以前曾经用你下面的建议成功编译过。我还没有成功编译(请参见https://dev59.com/1qrka4cB1Zd3GeqPgZ1p),但我已经接近了。我也会尝试使用CMake来使其工作。 感谢你的帮助:我好像忘记了一半关于Android NDK开发的知识! - Luther
如果你可以(几乎)使用ndk-build构建libanthracite-lib.so,那肯定更容易避免使用独立模块、预建库等。使用ndk-build完成所有工作会更加清晰,并且具有“自然”的库依赖关系。 - Alex Cohn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接