CMake 简单总结:
CMake 是一个跨平台的 构建系统生成工具 ,用于管理项目的编译、链接和打包流程。
通过 CMakeLists.txt
文件描述项目的源文件、依赖关系和编译选项。
注意:主要是在CMakeLists文件,简单说这个文件的作用是告诉编译器这些库的关系,还有打包自己的cpp文件成库(会将链接关系也会打包到生成的库文件中)
通过**target_link_libraries
** 明确指定你的代码需要哪些库。的作用是 声明链接关系 ,而非直接指定路径。
并且编译器和链接器在生成二进制文件时已经记录了这些依赖关系。
target_link_libraries
- 链接目标 :将当前目标(如
myfun
)与指定的库目标(如sdk
、${log-lib}
)关联。 - 传递依赖 :确保链接器能找到所有依赖库的符号(函数、变量)。
target_link_libraries(myfun # 目标名称(你的库或可执行文件)${log-lib} # 系统库(NDK 的 log 库)sdk # 你导入的库(路径由 IMPORTED_LOCATION 指定)dumpUtil # 其他库
)
生成的myfun.so 中会存在链接关系,当你在java 中加载了当前库文件。系统同时将这些依赖库文件也加载到内存中。
简单写个CMakeLists.txt
cmake_minmun_required(VERSION 3.10.2)
preject("myfun")INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SUORCE_DIR}/SDK/inc")
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/DumpUtil")file(GLOB src-files ${CMKE_CURRENT_SUORC_DIR}/*.cpp)
add_library(myfun SHARED ${src-files})find_library(log-lib log)add_library(sdk SHARED IMPORTED)
set_target_properties(sdk PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/sdk/lib/${ANDROID_ABI}/thirdPartyLib.so)target_link_libraries(myfun ${log-lib} sdk dumpUtil)
-
camke_minmum_required
: 指定最低CMake的版本(如VERSION 3.10) -
project
:定义项目名称(myfun) -
INCLUDE_DIRECTORIES
:添加头文件搜索路径。为cpp文件中#include
使用。 -
file(GLOB ...)
:根据通配符(如*.cpp
)匹配文件路径,并将结果存储到变量中。 -
add_library
:讲源文件编译为静态库.a
或者动态库.so
-
set_target_properties
: 批量设置目标(Target)属性 的命令,常用于配置编译、链接选项或导入预构建库的路径。-
导入预构建库
# 声明导入的共享库 add_library(sdk SHARED IMPORTED)# 设置库的路径(根据 ABI 动态适配) set_target_properties(sdk PROPERTIESIMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libsdk.so" )
-
修改输出目录
add_executable(my_app main.cpp)# 设置可执行文件的输出路径 set_target_properties(my_app PROPERTIESRUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" )
-
控制符号可见性
add_library(my_lib SHARED my_lib.cpp)# 隐藏内部符号(避免与其他库冲突) set_target_properties(my_lib PROPERTIESCXX_VISIBILITY_PRESET hiddenVISIBILITY_INLINES_HIDDEN ON )
-
-
find_library
:查找系统或第三方库的路径。 -
CMAKE_CURRENT_SUORCE_DIR
:指向包含当前正在执行的CMakeLists.txt
文件的目录路径。 -
CMAKE_SOURCE_DIR
:指向项目的顶层目录(即顶层CMakeLists.txt
所在的位置),无论当前正在处理哪个子目录的CMakeLists.txt
。 -
ANDROID_ABI
:表示当前构建针对的 ABI,如armeabi-v7a
、arm64-v8a
、x86
等。
配合Gradle使用
externalNativeBuild {cmake {path file('src/main/cpp/CMakeLists.txt')}}androidComponents {onVariants(selector().withBuildType("release")) {packaging.jniLibs.excludes.add('**/*thirdParty*.so')···}}
问:为什么Gradle中并为打包三方库到apk中,但以及可以使用呢?
因为 add_library(sdk SHARED IMPORTED)导入为动态库。而安卓加载动态库顺序为
- 应用的私有库目录 :/data/app/…/lib/ABI
- 系统库目录(/system/lib或/system/lib64)
- 其他系统路径(如/vendor/lib)
所以运行的时候可以使用。
问:为什么三方apk这样使用会报错呢?
三方应用apk 不是系统应用,无法访问到/system/lib64 下面的库文件,所以会抛出权限异常,无法使用。
问:什么是系统应用?
安装位置:系统应用安装在 /system/priv-app 或 /system/app 目录下(具体取决于权限级别)。
特性 | 系统应用 | 普通应用 |
---|---|---|
安装路径 | /system/priv-app/ 或/system/app/ | /data/app/ (用户数据分区) |
权限 | 可使用系统权限(如修改系统设置) | 仅限公开 API 和普通权限 |
动态库加载 | 可加载/system/lib64 中的库 | 仅限 APK 自带的lib/ 目录 |
卸载 | 无法通过常规方式卸载 | 用户可随时卸载 |
签名 | 需系统密钥签名 | 开发者自定义签名 |
问:为什么在CMakeLists中写的路径都是项目路径,但未打包的apk中但未报错呢?
我们需要知道编译时和运行时,编译时只是为了生成库并且在库中带有依赖关系,运行时是由apk 和系统环境决定的。
所以我们在写IMPORTED_LOCATION
等路径时候,要去确保项目中存在这些文件,保证编译正常通过。后续在运行时候 System.loadLibrary(myfun)
(加载我们生成的库文件时候)同时也会加载依赖库的库到内存中。(因为在我们自己的生成的库文件里面有写入了依赖关系,当加载该库时候同时也会将这些依赖库加载到内存中。)