Android studio CMake 学习记录
CMake
在android studio 2.2及以上,构建原生库的默认工具是 CMake。
CMake是一个跨平台的构建工具,可以用简单的语句来描述所有平台的安装(编译过程)。能够输出各种各样的makefile或者project文件。Cmake 并不直接建构出最终的软件,而是产生其他工具的脚本(如Makefile ),然后再依这个工具的构建方式使用。
CMake是一个比make更高级的编译配置工具,它可以根据不同平台、不同的编译器,生成相应的Makefile或者vcproj项目。从而达到跨平台的目的。Android Studio利用CMake生成的是ninja,ninja是一个小型的关注速度的构建系统。我们不需要关心ninja的脚本,知道怎么配置cmake就可以了。从而可以看出cmake其实是一个跨平台的支持产出各种不同的构建脚本的一个工具。
CMake的脚本名默认是CMakeLists.txt
1 #cmake最低版本 3 cmake_minimum_required(VERSION 3.6.0) 4 5 #指定项目 6 project(Main) 8 9 #生成可执行文件 main 10 add_executable(main main.c) 12 13 #执行cmake . 生成makefile 15 #再执行make即可生成main程序
如果源文件很多,那么一个个写进去是一件很麻烦的事情,这时候可以:
1 cmake_minimum_required(VERSION 3.6.0) 2 project(Main) 3 #查找当前目录所有源文件 并将名称保存到 DIR_SRCS 变量,但不能查找子目录 5 aux_source_directory(. DIR_SRCS) 6 message(${DIR_SRCS}) 7 8 #也可以 9 file(GLOB DIR_SRCS *.c) 10 add_executable(main ${DIR_SRCS})
如果在cmake中需要使用其他目录的cmakelist
1 cmake_minimum_required (VERSION 3.6.0) 2 project (Main) 3 aux_source_directory(. DIR_SRCS) 4 5 # 添加 child 子目录下的cmakelist 6 add_subdirectory(child) 7 8 # 指定生成目标 9 add_executable(main ${DIR_SRCS}) 10 11 # 添加链接库 12 target_link_libraries(main child) 13 14 #=========================================================================================== 15 #child目录下的cmake就是: 16 cmake_minimum_required (VERSION 3.6.0) 17 aux_source_directory(. DIR_LIB_SRCS) 18 19 # 生成链接库 默认生成静态库 20 add_library (child ${DIR_LIB_SRCS}) 21 22 #指定编译为静态库 23 add_library (child STATIC ${DIR_LIB_SRCS}) 24 25 #指定编译为动态库 26 add_library (child SHARED ${DIR_LIB_SRCS})
在上面的例子中都是生成可执行文件,让我们对cmakelist有了一定的了解。现在到android studio中使用cmakelist
1 #NDK中已经有一部分预构建库 ndk库已经是被配置为cmake搜索路径的一部分 所以可以 2 findLibrary(log-lib log) 3 target_link_libraries( native-lib ${log-lib} ) 4 5 #上述方法 其实直接这样也行: 6 target_link_libraries( native-lib log ) 7 8 #设置cflag和cxxflag 9 #定义预编译宏:TEST 10 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DTEST" ) 11 set(CMAKE_Cxx_FLAGS "${CMAKE_Cxx_FLAGS} -DTEST" )
添加其他预编译库(已经提前编译好的库)
1 #使用 IMPORTED 标志告知 CMake 只希望将库导入到项目中 2 #如果是静态库则将shared改为static 3 add_library( imported-lib 4 SHARED 5 IMPORTED ) 6 7 # 参数分别为:库、属性、导入地址、so所在地址 8 set_target_properties( 9 imported-lib 10 PROPERTIES 11 IMPORTED_LOCATION 12 ${CMAKE_SOURCE_DIR}/src/${ANDROID_ABI}/libimported-lib.so ) 13 14 #为了确保 CMake 可以在编译时定位头文件 15 #这样就可以使用 #include <xx> 引入 16 #否则需要使用 #include "path/xx" 17 include_directories( imported-lib/include/ ) 18 19 #native-lib 是自己编写的源码最终要编译出的so库 20 target_link_libraries(native-lib imported-lib) 21 22 #=========================================================================================== 23 #添加其他预编译库还可以使用这种方式 24 #使用-L指导编译时库文件的查找路径 25 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Lxx") 26 27 #为了确保 CMake 可以在编译时定位您的标头文件 28 include_directories( imported-lib/include/ ) 29 30 #native-lib 是自己编写的源码最终要编译出的so库 31 target_link_libraries(native-lib imported-lib)
include_directories()添加的范围大,target_include_directories()范围的范围可以自定义。如加关键子PRIVATE。一般引用库路径使用这个命令,作为外部依赖项引入进来,target一般是自己项目生成的lib。add_executable( )中添加的引用路径一般是当前目录下的源文件对应的头文件。
嵌套头文件的引用问题。
在子项目中,include的用法可以帮助当前环境找到头文件,但在外部的项目引用子项目时,子项目中的include的相关命令对外部项目无效,外部项目的引用只与他自己的include命令相关,因此,在子项目中的源文件中尽量使用绝对路径的文件。且在子项目的CMakeLists中尽量不要使用include_directories,因为它只针对子项目,外部项目没接受到include的文件,就会报引入错误。
如:
错误示范:
子项目:
include_directories(“../OrthophotoCU”)
源文件:
#include “global.h”
正确示范:
子项目:不加引入路径
源文件:
#include “../OrthophotoCU/global.h”
这样外部项目引入子项目时,就可以根据子项目的路径找到相对位置的头文件了。如果是错误情况,引入了子项目头文件,在子项目的相对路径下没有”global.h”。
常用CMake指令:
1 #set命令表示声明一个变量source 变量的值是后面的可变参数 2 set(source a b c) 3 message(${source}) 4 5 #逻辑判断 计较字符串 6 set(ANDROID_ABI "areambi-v7a") 7 if(${ANDROID_ABI} STREQUAL "areambi") 8 message("armv5") 9 elseif(${ANDROID_ABI} STREQUAL "areambi-v7a") 10 message("armv7a") 11 else() 12 endif()
//还可以在gradle中使用 arguments 设置一些配置
1 externalNativeBuild { 2 cmake { 3 arguments "-DANDROID_TOOLCHAIN=clang", //使用的编译器clang/gcc 4 "-DANDROID_STL=gnustl_static" //cmake默认就是 gnustl_static 可换成c++_static 5 cFlags "" //这里也可以指定cflag和cxxflag,效果和之前的cmakelist里使用一样 6 cppFlags "" 7 targets '' 8 } 9 }
5.0及以下与6.0及以上的注意事项:
存在两个动态库libhello-jni.so 与 libTest.so。
libhello-jni.so依赖于libTest.so (使用NDK下的ndk-depends可查看依赖关系),则:
1 //<=5.0: 2 System.loadLibrary("Test"); 3 System.loadLibrary("hello-jni"); 4 5 //>=6.0: 6 System.loadLibrary("hello-jni");
Android.mk
使用Android.mk在 >=6.0 设备上不能再使用预编译动态库(静态库没问题):
1 LOCAL_PATH := $(call my-dir) 2 include $(CLEAR_VARS) 3 LOCAL_MODULE := Test 4 5 #libTest.so放在当前文件同目录 6 LOCAL_SRC_FILES := libTest.so 7 8 #预编译库 9 include $(PREBUILT_SHARED_LIBRARY) 10 include $(CLEAR_VARS) 11 12 #引入上面的Test模块 13 LOCAL_SHARED_LIBRARIES := Test 14 LOCAL_MODULE := hello-jni 15 LOCAL_SRC_FILES := hello-jni.c 16 include $(BUILD_SHARED_LIBRARY)
上面这段配置生成的libhllo-jni在>=6.0设备中无法执行。
CMake
使用CMakeList.txt在 >=6.0 设备上引入预编译动态库:
1 cmake_minimum_required(VERSION 3.4.1) 2 file(GLOB SOURCE *.c ) 3 add_library( 4 hello-jni 5 SHARED 6 ${SOURCE} 7 ) 8 9 #这段配置在6.0依然没问题 10 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L[SO所在目录]") 11 12 #这段配置只能在6.0以下使用 原因和android.mk一样 13 #add_library(Test SHARED IMPORTED) 14 #set_target_properties(Test PROPERTIES IMPORTED_LOCATION [SO绝对地址]) 15 target_link_libraries( hello-jni Test )
浙公网安备 33010602011771号