Rust 交叉编译 Android 共享库

接上篇

https://www.cnblogs.com/develon/p/16464371.html

错过了什么?

事实上, 下面两种方式都是可行的:

add_library( rust SHARED IMPORTED GLOBAL)
set_property( TARGET rust PROPERTY IMPORTED_NO_SONAME 1 )
set_property( TARGET rust PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/rust/librust.so )

以及

target_link_directories(tcp2ws PUBLIC ${CMAKE_CURRENT_LIST_DIR}/rust)

但是已构建的共享库并不会打包进Apk, maybe, 需要手动设置 Gradle 脚本?
同时, 应该使用到变量 ${CMAKE_ANDROID_ARCH_ABI} 或者 ${ANDROID_ABI}.

插话

可以使用 message 调试cmake:

message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")
message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")

点击Sync Now,可以看到 CMAKE 输出:

> Task :prepareKotlinBuildScriptModel UP-TO-DATE
C/C++: debug|arm64-v8a :CMAKE_CURRENT_LIST_DIR: Z:/AndroidProjs/Tcp2ws/app/src/main/cpp
C/C++: debug|arm64-v8a :CMAKE_SOURCE_DIR: Z:/AndroidProjs/Tcp2ws/app/src/main/cpp

BUILD SUCCESSFUL in 1s

Gradle 打包共享库

愚蠢!官方文档居然隐藏如此之深:https://developer.android.com/studio/projects/gradle-external-native-builds?hl=zh-cn#jniLibs
还是在 Github issues 中找到的:https://github.com/android/ndk-samples/issues/855

如果您希望 Gradle 打包未在任何外部原生 build 中使用的预构建原生库,请将其添加到模块的 src/main/jniLibs/ABI 目录中。

4.0 之前的 Android Gradle 插件版本要求 CMake IMPORTED 目标必须包含在 jniLibs 目录中才能将这些目标纳入应用。如果您要从该插件的较低版本进行迁移,可能会遇到如下错误:
* What went wrong:
Execution failed for task ':app:mergeDebugNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > More than one file was found with OS independent path 'lib/x86/libprebuilt.so'

如果您使用的是 Android Gradle 插件 4.0,请将 IMPORTED CMake 目标使用的所有库从 jniLibs 目录移出,以避免出现此错误。

既然如此,我们将 src\main\cpp\rust 的输出目录设为 src\main\jniLibs\ABI :

RUSTC_BOOTSTRAP=1 cargo build --target "aarch64-linux-android" -Z unstable-options --out-dir ../../jniLibs/arm64-v8a

那么 CMAKE 如何引用该目录呢?暂时先手动:

message("jniLibs: ${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
target_link_directories(tcp2ws PUBLIC ${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI})

对了,能否自定义 jniLibs 目录呢?很明显可以:

android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs', 'src/main/jniLibs'] // 相对路径为app,建议还是创建一个默认的 app\src\main\jniLibs 目录比较方便
        }
    }
}

总结,我们需要这样设置:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("tcp2ws")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        tcp2ws

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

#add_library( rust SHARED IMPORTED )
#add_library( rust SHARED IMPORTED GLOBAL)
#set_property( TARGET rust PROPERTY IMPORTED_NO_SONAME 1 )  # IMPORTED_NO_SONAME 解决ld绝对路径问题,依赖该共享库的共享库加载时不应以绝对路径去加载
#set_property( TARGET rust PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/rust/librust.so )
#set_target_properties( rust PROPERTIES IMPORTED_NO_SONAME 1 IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/rust/librust.so )

message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")  # C++目录
message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")  # 也是C++目录
message("ABI: ${CMAKE_ANDROID_ARCH_ABI} / ${ANDROID_ABI}")
message("CMAKE_LIBRARY_OUTPUT_DIRECTORY: ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")  # 该目录是C++编译输出目录
set(jniLibs ${CMAKE_SOURCE_DIR}/../jniLibs)
message("jniLibs: ${jniLibs}")
target_link_directories(tcp2ws PUBLIC ${jniLibs}/${CMAKE_ANDROID_ARCH_ABI})

# 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( # Specifies the target library.
        tcp2ws

#        ${CMAKE_CURRENT_LIST_DIR}/rust/librust.a  # 静态库没有问题
        rust  # 该外部预构建库会在target_link_directories中查找

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

总结: 3钟方法

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("tcp2ws")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        tcp2ws

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

message("CMAKE_FIND_ROOT_PATH: ${CMAKE_FIND_ROOT_PATH}") #S:/SDK/ndk/21.4.7075529
message("CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") #S:/SDK/ndk/21.4.7075529/toolchains/llvm/prebuilt/windows-x86_64
message("CMAKE_SYSROOT: ${CMAKE_SYSROOT}") #S:/SDK/ndk/21.4.7075529/toolchains/llvm/prebuilt/windows-x86_64/sysroot
message("CMAKE_FIND_ROOT_PATH_MODE_INCLUDE : ${CMAKE_FIND_ROOT_PATH_MODE_INCLUDE}") #ONLY
message("CMAKE_FIND_ROOT_PATH_MODE_LIBRARY : ${CMAKE_FIND_ROOT_PATH_MODE_LIBRARY}") #ONLY, 只有CMAKE_FIND_ROOT_PATH中的根会被搜索到。
#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) #这个变量控制find_file()和find_path()是否使用CMAKE_FIND_ROOT_PATH和CMAKE_SYSROOT。
#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) #这个变量控制find_library()是否使用CMAKE_FIND_ROOT_PATH和CMAKE_SYSROOT。
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# 链接外部共享库
# 方法1: IMPORTED + IMPORTED_NO_SONAME + IMPORTED_LOCATION
#add_library( rust SHARED IMPORTED )
#add_library( rust SHARED IMPORTED GLOBAL)
#set_property( TARGET rust PROPERTY IMPORTED_NO_SONAME 1 )
#set_property( TARGET rust PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/rust/librust.so )
#set_target_properties( rust PROPERTIES IMPORTED_NO_SONAME 1 IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/rust/librust.so )

message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}") #Z:/AndroidProjs/Tcp2ws/app/src/main/cpp
message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") #Z:/AndroidProjs/Tcp2ws/app/src/main/cpp
message("ABI: ${CMAKE_ANDROID_ARCH_ABI} / ${ANDROID_ABI}") #arm64-v8a / arm64-v8a
message("CMAKE_LIBRARY_ARCHITECTURE: ${CMAKE_LIBRARY_ARCHITECTURE}") #aarch64-linux-android
message("CMAKE_ANDROID_ARCH: ${CMAKE_ANDROID_ARCH}") #arm64
message("CMAKE_LIBRARY_OUTPUT_DIRECTORY: ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") #Z:\AndroidProjs\Tcp2ws\app\build\intermediates\cxx\Debug\4o6a5p4o\obj\arm64-v8a
set(jniLibs ${CMAKE_SOURCE_DIR}/../jniLibs)
message("jniLibs: ${jniLibs}")

# 方法2: target_link_directories
#target_link_directories(tcp2ws PUBLIC ${jniLibs}/${CMAKE_ANDROID_ARCH_ABI})

# 方法3: find_library()
# find_library将搜索该目录下的lib目录, 参考: https://stackoverflow.com/questions/12075371/cmake-find-library-custom-library-location
set(CMAKE_FIND_ROOT_PATH ${jniLibs}/${CMAKE_ANDROID_ARCH_ABI})
set(CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX ../) # 为了删除 lib 目录, 设置该前缀
message("CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX: ${CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX}")
find_library(
        rust-lib
        NAMES rust
#        以下参数无效
#        HINTS [path | ENV var]...]
#        [PATHS [path | ENV var]...]
#        PATH_SUFFIXES suffix1
#        NO_DEFAULT_PATH
)
message("rust-lib: ${rust-lib}") #Z:/AndroidProjs/Tcp2ws/app/src/main/cpp/../jniLibs/arm64-v8a/Windows/../librust.so

# 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( # Specifies the target library.
        tcp2ws

#        ${CMAKE_CURRENT_LIST_DIR}/rust/librust.a
#        rust
        ${rust-lib}

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

针对 ABI 配置多个 APK

https://developer.android.com/studio/build/configure-apk-splits?hl=zh-cn#configure-abi-split

一些参考

https://github.com/android/ndk/issues/928

posted @ 2023-06-01 20:35  develon  阅读(315)  评论(1编辑  收藏  举报