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