51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Android中CMake语法介绍和简单使用

引言 {#引言}

CMake是一个跨平台开源的构建系统。它是一个集软件构建、测试、打包于一身的软件,可以用简单的语句来描述所有平台的安装编译过程。

CMake的所有的语句都写在一个叫CMakeLists.txt的文件中。当CMakeLists.txt文件确定后,可以用ccmake命令对相关的变量值进行配置。

CMake 的基本语法规则 {#CMake-的基本语法规则}

  • 使用星号 # 作为注释;
  • 变量使用 ${} 方式取值,但是在 IF 控制语句中是直接使用变量名;
  • 指令名(参数1 参数2 ...),其中参数之间使用空格或分号隔开;
  • 指令与大小写无关,但参数和变量是大小写相关的;

CMake 的常用指令 {#CMake-的常用指令}

注:指令与大小写无关,官方建议使用大写,不过 Android 的 CMake 指令是小写的,下面为了便于阅读,采取小写的方式。

project 指令 {#project-指令}

语法:project( [CXX] [C] [Java])
这个指令是定义工程名称的,并且可以指定工程支持的语言(当然也可以忽略,默认情况表示支持所有语言),不是强制定义的。例如:project(HELLO)
定义完这个指令会隐式定义了两个变量:
<projectname>_BINARY_DIR<projectname>_SOURCE_DIR
由上面的例子也可以看到,MESSAGE 指令有用到这两个变量;

另外 CMake 系统还会预定义了 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR 变量,它们的值和上面两个的变量对应的值是一致的。不过为了统一起见,建议直接使用PROJECT_BINARY_DIRPROJECT_SOURCE_DIR,即使以后修改了工程名字,也不会影响两个变量的使用。

set指令 {#set指令}

语法:set(VAR [VALUE])

这个指令是用来显式地定义变量,多个变量用空格或分号隔开

例如:set(SRC_LIST main.c test.c)

注意,当需要用到定义的 SRC_LIST 变量时,需要用v a r 的 形 式 来 引 用 , 如 : {var}的形式来引用,如:var的形式来引用,如:{SRC_LIST}
不过,在 IF 控制语句中可以直接使用变量名。

message 指令 {#message-指令}

语法:message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ... )
这个指令用于向终端输出用户定义的信息,包含了三种类型:
SEND_ERROR,产生错误,生成过程被跳过;
STATUS,输出前缀为----的信息;(由上面例子也可以看到会在终端输出相关信息)
FATAL_ERROR,立即终止所有 CMake 过程;

add_executable 指令 {#add-executable-指令}

语法:add_executable(executable_file_name [source])
将一组源文件 source 生成一个可执行文件。 source 可以是多个源文件,也可以是对应定义的变量
如:add_executable(hello main.c)

cmake_minimun_required(VERSION 3.4.1) {#cmake-minimun-required-VERSION-3-4-1}

用来指定 CMake 最低版本为3.4.1,如果没指定,执行 cmake 命令时可能会出错

add_subdirectory 指令 {#add-subdirectory-指令}

语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL参数含义是将这个目录从编译过程中排除。

另外,也可以通过 SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

add_library 指令 {#add-library-指令}

语法:add_library(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] [source])
将一组源文件 source 编译出一个库文件,并保存为 libname.so (lib 前缀是生成文件时 CMake自动添加上去的)。其中有三种库文件类型,不写的话,默认为 STATIC:

  • SHARED: 表示动态库,可以在(Java)代码中使用 System.loadLibrary(name) 动态调用;
  • STATIC: 表示静态库,集成到代码中会在编译时调用;
  • MODULE: 只有在使用 dyId 的系统有效,如果不支持 dyId,则被当作 SHARED 对待;
  • EXCLUDE_FROM_ALL: 表示这个库不被默认构建,除非其他组件依赖或手工构建

|-------------|--------------------------------------------------------------------------------------| | 1 2 | #将compress.c 编译成 libcompress.so 的共享库 add_library(compress SHARED compress.c) |

add_library 命令也可以用来导入第三方的库:

|-----------|----------------------------------------------| | 1 | add_library(libjpeg SHARED IMPORTED) |

导入库后,当需要使用 target_link_libraries 链接库时,可以直接使用该库

find_library 指令 {#find-library-指令}

语法:find_library( name1 path1 path2 ...)
VAR 变量表示找到的库全路径,包含库文件名 。例如:

|-------------|-----------------------------------------------------------------------------------------| | 1 2 | find_library(libX X11 /usr/lib) find_library(log-lib log) #路径为空,应该是查找系统环境变量路径 |

set_target_properties 指令 {#set-target-properties-指令}

语法: set_target_properties(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
这条指令可以用来设置输出的名称(设置构建同名的动态库和静态库,或者指定要导入的库文件的路径),对于动态库,还可以用来指定动态库版本和 API 版本。
如,set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
设置同名的 hello 动态库和静态库:

|-------------|--------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 | set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) |

指定要导入的库文件的路径

|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 | add_library(jpeg SHARED IMPORTED) #注意要先 add_library,再 set_target_properties set_target_properties(jpeg PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg.so) |

设置动态库 hello 版本和 API 版本:
set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
和它对应的指令:
get_target_property(VAR target property)
如上面的例子,获取输出的库的名字

|-------------|-------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 | get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME) message(STATUS "this is the hello_static OUTPUT_NAME:"${OUTPUT_VALUE}) |

include_directories 指令 {#include-directories-指令}

语法:include_directories([AFTER | BEFORE] [SYSTEM] dir1 dir2...)
这个指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的
后面。

target_link_libraries 指令 {#target-link-libraries-指令}

语法:target_link_libraries(target library <debug | optimized> library2...)
这个指令可以用来为 target 添加需要的链接的共享库,同样也可以用于为自己编写的共享库添加共享库链接。
如:

|-------------|-------------------------------------------------------------------------------------------------| | 1 2 | #指定 compress 工程需要用到 libjpeg 库和 log 库 target_link_libraries(compress libjpeg ${log-lib}) |

同样,link_directories(directory1 directory2 ...) 可以添加非标准的共享库搜索路径。

file 指令 {#file-指令}

CMake 中通过 file 来实现文件操作,包括文件读写、下载文件、文件重命名等

|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 | # 文件重命名 file(RENAME "test.txt" "new.txt") # 文件下载 # 把文件 URL 设定为变量 set(var "http://img.zcool.cn/community/0117e2571b8b246ac72538120dd8a4.jpg") # 使用 DOWNLOAD 下载 file(DOWNLOAD ${var} "/Users/glumes/CLionProjects/HelloCMake/image.jpg") |

在文件的操作中,还有两个很重要的指令 GLOB 和 GLOB_RECURSE 。

|-----------------|------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 | # GLOB 的使用 file(GLOB ROOT_SOURCE *.cpp) # GLOB_RECURSE 的使用 file(GLOB_RECURSE CORE_SOURCE ./detail/*.cpp) |

其中,GLOB 指令会将所有匹配 *.cpp 表达式的文件组成一个列表,并保存在 ROOT_SOURCE 变量中。

而 GLOB_RECURSE 指令和 GLOB 类似,但是它会遍历匹配目录的所有文件以及子目录下面的文件。

使用 GLOB 和 GLOB_RECURSE 有好处,就是当添加需要编译的文件时,不用再一个一个手动添加了,同一目录下的内容都被包含在对应变量中了,但也有弊端,就是新建了文件,但是 CMake 并没有改变,导致在编译时也会重新产生构建文件,要解决这个问题,就是动一动 CMake,让编译器检测到它有改变就好了。

还有其他 list、install 、find_ 指令和控制指令等就不介绍了,详细可以查看手册。

配置说明 {#配置说明}

工程路径 #nativelib和app为module名 {#工程路径-nativelib和app为module名}

|---------------|-------------------------------------------------------------------------------------------------------------| | 1 2 3 | set(pathToProject ${PROJECT_SOURCE_DIR}/nativelib) set(pathToProject D:/Android/Code/MyProject/app) |

指定 cmake 的最小版本 {#指定-cmake-的最小版本}

|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 | cmake_minimum_required(VERSION 3.4.1) #支持-std=gnu++11 set(CMAKE_VERBOSE_MAKEFILE on) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") |

加载native头文件依赖 {#加载native头文件依赖}

|-----------------------|----------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 | include_directories( ${pathToProject}/src/main/cpp/include ) #动态加载库(以librf001.so为例) add_library( rf001 SHARED IMPORTED ) |

引入库文件(以librf001.so为例) {#引入库文件(以librf001-so为例)}

|-------------------|----------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 | set_target_properties( rf001 PROPERTIES IMPORTED_LOCATION ${pathToProject}/src/main/jniLibs/${ANDROID_ABI}/librf001.so ) |

明确指定包含哪些源文件 {#明确指定包含哪些源文件}

|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | add_library( # Sets the name of the library. demo # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/libMifare.cpp src/main/cpp/util/Exception.cpp ) add_library( # Sets the name of the library. LibUart # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/LibUart.cpp src/main/cpp/util/Exception.cpp src/main/cpp/uart/Uart.cpp ) |

查找指定的库文件 {#查找指定的库文件}

|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 | 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 2 3 4 5 6 7 8 9 10 | target_link_libraries( # 目标库 demo # 目标库需要链接的库 # log-lib 是上面 find_library 指定的变量名 ${log-lib} ) target_link_libraries( # Specifies the target library. LibUart # Links the target library to the log library # included in the NDK. ${log-lib} ) |

指定链接多个库 {#指定链接多个库}

|-------------------|--------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 | target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a boost_system.a boost_thread pthread) |

常用变量 {#常用变量}

变量引用方式 {#变量引用方式}

使用 ${} 进行变量的引用。不过在 IF 等语句中,可以直接使用变量名而不用通过 ${} 取值

自定义变量的方式 {#自定义变量的方式}

主要有隐式定义和显式定义两种。隐式定义,如 PROJECT 指令会隐式定义_BINARY_DIR 和 _SOURCE_DIR
而对于显式定义就是通过 SET 指令来定义。如:set(HELLO_SRC main.c)

预定义变量 {#预定义变量}

  • PROJECT_SOURCE_DIR:工程的根目录
  • PROJECT_BINARY_DIR:运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build
  • PROJECT_NAME:返回通过 project 命令定义的项目名称
  • CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的路径
  • CMAKE_CURRENT_BINARY_DIR:target 编译目录
  • CMAKE_CURRENT_LIST_DIR:CMakeLists.txt 的完整路径
  • CMAKE_CURRENT_LIST_LINE:当前所在的行
  • CMAKE_MODULE_PATH:定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块
  • EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置
  • LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置

系统信息 {#系统信息}

  • CMAKE_MAJOR_VERSION:cmake 主版本号,比如 3.4.1 中的 3
  • CMAKE_MINOR_VERSION:cmake 次版本号,比如 3.4.1 中的 4
  • CMAKE_PATCH_VERSION:cmake 补丁等级,比如 3.4.1 中的 1
  • CMAKE_SYSTEM:系统名称,比如 Linux-2.6.22
  • CMAKE_SYSTEM_NAME:不包含版本的系统名,比如 Linux
  • CMAKE_SYSTEM_VERSION:系统版本,比如 2.6.22
  • CMAKE_SYSTEM_PROCESSOR:处理器名称
  • UNIX:在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin
  • WIN32:在所有的 win32 平台下该值为 TRUE,包括 cygwin

CMake 常用变量说明 {#CMake-常用变量说明}

  • 1)CMAKE_BINARY_DIR, PROJECT_BINARY_DIR, _BINARY_DIR
    这三个变量指代的内容都是一样的,如果是 in-source 编译,指的是工程顶层目录,如果是 out-of-source 编译,指的是工程编译发生的目录。
  • 2)CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR, _SOURCE_DIR
    这三个变量指代的内容也是一样的,不论哪种编译方式,都是工程顶层目录。 当前工程的Cmake路径
  • 3)CMAKE_CURRENT_SOURCE_DIR
    当前处理的 CMakeLists.txt 所在的路径 当前CMake文件所在的文件夹路径
  • 4)CMAKE_CURRENT_BINARY_DIR
    如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 编译,指的是 target 编译目录。
    使用 ADD_SUBDIRECTORY(src bin)可以修改这个变量的值;而使用 SET(EXECUTABLE_OUTPUT_PATH < 新路径>) 并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。
  • 5)CMAKE_CURRENT_LIST_FILE
    输出调用这个变量的 CMakeLists.txt 的完整路径
  • 6)CMAKE_CURRENT_LIST_LINE
    输出这个变量所在的行
  • 7)CMAKE_MODULE_PATH
    这个变量用来定义自己的 CMake 模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设
    置一下。
    比如 SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
    这时候你就可以通过 INCLUDE 指令来调用自己的模块了。
  • 8)EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
    分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。
  • 9)PROJECT_NAME
    返回通过 PROJECT 指令定义的项目名称。

Android CMake 的使用 {#Android-CMake-的使用}

CMakeList.txt 的编写 {#CMakeList-txt-的编写}

Android NDK 开发中 CMake 的使用,先看一个系统生成的 NDK 项目的 CMakeLists.txt 的配置:( 去掉原有的注释)

|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 | #设置编译 native library 需要最小的 cmake 版本 cmake_minimum_required(VERSION 3.4.1) #将指定的源文件编译为名为 libnative-lib.so 的动态库 add_library(native-lib SHARED src/main/cpp/native-lib.cpp) #查找本地 log 库 find_library(log-lib log) #将预构建的库添加到自己的原生库 target_link_libraries(native-lib ${log-lib} ) |

复杂一点的 CMakeLists,这是一个本地使用 libjpeg.so 来做图片压缩的项目

|------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | cmake_minimum_required(VERSION 3.4.1) #设置生成的so动态库最后输出的路径 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}) #指定要引用的libjpeg.so的头文件目录 set(LIBJPEG_INCLUDE_DIR src/main/cpp/include) include_directories(${LIBJPEG_INCLUDE_DIR}) #导入libjpeg动态库 SHARED;静态库为STATIC add_library(jpeg SHARED IMPORTED) #对应so目录,注意要先 add_library,再 set_target_properties) set_target_properties(jpeg PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg.so) add_library(compress SHARED src/main/cpp/compress.c) find_library(graphics jnigraphics) find_library(log-lib log) #添加链接上面个所 find 和 add 的 library target_link_libraries(compress jpeg ${log-lib} ${graphics}) |

配置 Gradle {#配置-Gradle}

简单的配置如下,至于 cppFlags 或 cFlags 的参数有点复杂,一般设置为空或不设置也是可以的,这里就不过多介绍了

|---------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | android { compileSdkVersion 25 buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" externalNativeBuild { cmake { // Passes optional arguments to CMake. arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang" // Sets optional flags for the C compiler. cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2" // Sets a flag to enable format macro constants for the C++ compiler. cppFlags "-D__STDC_FORMAT_MACROS" //生成.so库的目标平台 abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a' } } } //配置 CMakeLists.txt 路径 externalNativeBuild { cmake { path "CMakeLists.txt" } } } |

参考链接:https://blog.csdn.net/yinianzhijian99/article/details/122865256
参考链接:https://blog.csdn.net/xiaomaothm/article/details/124020250


赞(4)
未经允许不得转载:工具盒子 » Android中CMake语法介绍和简单使用