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 )

 

posted on 2019-10-20 10:23  TXZH  阅读(490)  评论(0)    收藏  举报

导航