【问题标题】:Android CMake use prebuild .a libraryAndroid CMake 使用 prebuild .a 库
【发布时间】:2018-07-05 10:12:26
【问题描述】:

我对 CMake 完全陌生,并且共同使用 NDK。我想出编写​​我的 JNI 接口并使用 C 库中的 2 个方法。我将此库编译为静态库并获得了 .a 文件。现在我有点迷失了,因为我不明白如何告诉 Android Studio 在尝试查找调用的函数时使用这个库。

这是我当前的 CMakeLists.txt,它位于“app”模块文件夹中。

cmake_minimum_required(VERSION 3.4.1)

add_library(my-lib SHARED src/main/cpp/my-lib.cpp )

target_link_libraries(my-lib z crypto)

target_link_libraries(my-lib ${CMAKE_CURRENT_SOURCE_DIR}/../libs/libmine.a)

在编译时,我收到警告说找不到对被调用函数的引用。我的 CMakeLists.txt 是否正确?如何包含函数的 .h 文件?提前感谢您的帮助!

【问题讨论】:

  • 你用什么工具链来构建你的静态库?交叉编译时需要使用Android工具链编译所有代码。
  • 我在 Ubuntu VM 上使用 gcc 编译它。如何直接使用 CMake 编译它?
  • 这里有文档记录:developer.android.com/ndk/guides/standalone_toolchain 请记住,为 Android 构建通常是针对各种 ARM 芯片(和其他)进行交叉编译。
  • 谢谢,我去看看

标签: android c++ cmake java-native-interface


【解决方案1】:

我不明白在尝试查找被调用函数时如何告诉 Android Studio 使用此库

为了使用您的本机库,即libmy-lib.so,您需要在您的 java 部分中加载此共享库,如下所示。

    static {
        System.loadLibrary("my-lib");
    }

我的 CMakeLists.txt 是否正确?

是的,它是正确的,但并不完美。

以及如何包含函数的 .h 文件

为了方便自己添加头文件includes,需要稍微配置一下CMakelists.txt。例如。您可能有如下目录结构,如果您只有app/src/main/cpp,则可以删除那些不相关的目录和配置。

    app
    ├── CMakeLists.txt
    └── src
        ├── foo
        │   ├── CMakeLists.txt
        │   ├── foo.cpp
        │   └── foo.h
        ├── main
        │   └── cpp
        │       ├── CMakeLists.txt
        │       └── my-lib.cpp
        └── test
            ├── CMakeLists.txt
            └── google_test_classXXX.cpp

然后您需要如下配置您的app/CMakelists.txt

    # set the root directory as ${CMAKE_CURRENT_SOURCE_DIR} which is a
    # CMAKE build-in function to return the current dir where your CMakeLists.txt is. 
    # Specifically, it is "<your-path>/App/"
    set(APP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})

    # set your 3 other root dirs, i.e. foo, main and test under app/src.
    set(APP_ROOT_SRC_DIR ${APP_ROOT_DIR}/src)
    set(APP_ROOT_FOO_DIR ${APP_ROOT_SRC_DIR}/foo)
    set(APP_ROOT_MAIN_DIR ${APP_ROOT_SRC_DIR}/main)
    set(APP_ROOT_TEST_DIR ${APP_ROOT_SRC_DIR}/test)

    # set your include paths into "SHARED_INCLUDES" variable so that you can quote your header file without adding its relative paths. 
    set(SHARED_INCLUDES
                    ${APP_ROOT_FOO_DIR}
                    # ${APP_ROOT_FOO_DIR}/<your-other-child-dirs>

                    ${APP_ROOT_MAIN_DIR}
                    ${APP_ROOT_MAIN_DIR}/cpp
                    # ${APP_ROOT_MAIN_DIR}/<your-other-child-dirs>

                    ${APP_ROOT_TEST_DIR}
                    # ${APP_ROOT_TEST_DIR}/<your-other-child-dirs>
                    )

    # This function will have effect to all the downstream cmakelist files. 
    include_directories(${SHARED_INCLUDES})

    add_library(my-lib SHARED src/main/cpp/my-lib.cpp )

    target_link_libraries(my-lib z crypto)

    target_link_libraries(my-lib ${CMAKE_CURRENT_SOURCE_DIR}/../libs/libmine.a)

    # remember to include downstream cmakelist files for foo, main and test.
    add_subdirectory(${APP_ROOT_FOO_DIR} bin-dir)
    add_subdirectory(${APP_ROOT_MAIN_DIR} bin-dir)
    add_subdirectory(${APP_ROOT_TEST_DIR} bin-dir)

----已编辑----

关于如何链接 prebuild .a 库。

    # Specifies libraries CMake should link to your target library. You
    # can link multiple libraries, such as libraries you define in this
    # build script, prebuilt third-party libraries, or system libraries.
    target_link_libraries(my-lib -Wl,--whole-archive ${CMAKE_CURRENT_SOURCE_DIR}/../libs/libmine.a -Wl,--no-whole-archive)

----已编辑以回答您的三个问题----

cpp 目录中的 CMakeLists.txt 的一部分是什么? 需要在cpp目录下还是在主目录下?

理论上,您的所有源代码目录和标头目录都可以只有一个CMakelists.txt,但是一旦您的项目发展到非常大的规模,这个一体化的CMakelists.txt 将变得相当复杂且不可读并且可维护。通常,每个 cmake 模块都应该有自己的CMakeLists.txt 文件,以便模块化并且更易于管理。例如。 cpp 目录有一个CMakeLists.txt 来管理其所有子目录(如果有),maintest“模块”也是如此。

以及如何包含我的 .a 库的 .h 文件 - #include 不起作用。

正如我上面提到的,您需要配置SHARED_INCLUDES 以将您的相对路径添加到您的.a 的标头(.h),这样您就可以简单地使用#include &lt;xxx.h&gt; 来包含标头。

    set(SHARED_INCLUDES
                ${APP_ROOT_FOO_DIR}
                # ${APP_ROOT_FOO_DIR}/<your-other-child-dirs>

                ${APP_ROOT_MAIN_DIR}
                ${APP_ROOT_MAIN_DIR}/cpp
                # ${APP_ROOT_MAIN_DIR}/<your-other-child-dirs>

                ${APP_ROOT_TEST_DIR}
                # ${APP_ROOT_TEST_DIR}/<your-other-child-dirs>
       )

将您的包含路径设置为“SHARED_INCLUDES”变量,以便您可以引用您的头文件而不添加其相对路径。


编辑以回答您关于如何配置架构的问题

您可以在build.gradle 中配置您的目标,如下所示:

    defaultConfig {
        externalNativeBuild {
            cmake {
                ...
                abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
                ...
            }
        }
    }

CMake 构建过程将逐个处理每个 ABI。 CMakelists.txt 中的变量 ${ANDROID_ABI} 可以告诉您它正在构建的当前 ABI(架构)。如果需要,您还可以使用此变量来配置库 PATH。

例如这个变量${ANDROID_ABI}里面

target_link_libraries(${SHARED_LIBRARY_NAME} -Wl,--whole-archive ${CMAKE_CURRENT_SOURCE_DIR}/../libs/${ANDROID_ABI}/libmine.a -Wl,--no-whole-archive)

在构建期间将替换为 armeabi-v7aarm64-v8ax86x86_64

【讨论】:

  • 非常感谢您的详细解答。三个问题:cpp 目录中的 CMakeLists.txt 的一部分是什么?它需要在cpp目录中还是在主目录中?以及如何包含我的 .a 库的 .h 文件 - #include 不起作用。
  • 这意味着我必须将头文件保留在应用程序中,对吗?我想我误解了这一点,之前没有添加它们
  • 你可以把你的头文件放在任何你喜欢的地方,但是记得把目录路径添加到你的include_directories中,这样你就不必添加它的相对路径
  • 好的,谢谢,这有帮助。还有一个问题:CMake 中有没有办法告诉 target_link_libraries 我们在哪个架构上?我包括了 arm64-v8a、armeabi-v7a、x86 和 x86_64,但是是否有一个变量,所以 CMake 知道它应该采用哪个变量?
  • 再次感谢您的详细回答:)
【解决方案2】:

首先你需要指定包含目录以及使用 include_directories() 命令。 其次,您似乎想将 static 库(.a)与您的最终 shared 库链接。这是无法做到的。你要么需要共享库,要么需要静态库。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多