add_executable(hello 
                world.c 
                foo.c)
#这是一个注释 

就是这么easy。注意参数这一块,可以用引号包裹起来,这代表一个参数,假如一行不能写完,则用//符号来表示连接成一行,也可以不用引号,但是假如参数带有分隔符,则会被认为是多个参数。

定义变量

定义变量常用的函数是set(KEY VALUE),取消定义变量是unset(KEY)。 它们的值始终是string类型的,有些命令可能将字符串解释为其他类型的值。变量名是区分大小写的,可能包含任何文本,但是我们建议只使用字母数字字符加上_和-这样的名称。

变量的作用域和java基本一致,不多做讲解。

变量引用的形式为${variable_name},并在引用的参数或未引用的参数中进行判断。变量引用被变量的值替换,或者如果变量没有设置,则由空字符串替换。变量引用可以嵌套,并从内部进行替换,例如${outer_${inner_variable}veriable}。 环境变量引用的形式为$ENV{VAR},并在相同的上下文中作为正常变量引用。

cmake构建系统

这算是进入正文了。扯淡的介绍就不多讲了,直接干货。

可执行文件和库是使用add_executable()和add_library()命令定义的。生成的二进制文件有合适的前缀、后缀和针对平台的扩展。二进制目标之间的依赖关系使用target_link_libraries()命令表示。

add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive) 

在构建c程序的时候,因为要生成可执行文件,add_executable是必须的;构建安卓动态库的时候,add_library是必须的,因为jni需要调用动态库。

外部构建

前边提到过cmake的构建命令cmake .,也就是在当前目录构建工程,这样会生成一系列的缓存文件在当前目录,假如我们需要重新构建需要删除这些文件,其中CMakeCache.txt是必须删除的,否则不会构建最新的程序。 看一下我们在工程根目录下执行cmake后是什么情况:

? ~/Desktop/Tutorial/Step1/ tree -L 1
.
├── CMakeCache.txt
├── CMakeFiles
├── CMakeLists.txt
├── Makefile
├── Tutorial
├── TutorialConfig.h
├── TutorialConfig.h.in
├── cmake_install.cmake
└── tutorial.cxx 

生成的文件与源文件交叉在一起,相当混乱。我们可以采用外部构建来分隔源文件与生成的文件,当我们需要清空缓存重新构建项目时,就可以删除这个文件夹下的所有内容,重新运行构建命令,保持源文件的整洁,从而更容易管理项目。

首先新建一个文件夹build。这个文件夹就是我们用来存放生成的文件的目录,然后进入该目录,执行构建命令。

mkdir build # 创建build目录
cd build # 进入build目录
cmake .. # 因为程序入口构建文件在项目根目录下,采用相对路径上级目录来使用根目录下的构建文件 

此时可以看到生成的文件全部在build文件夹下了,构建完全没问题。

? ~/Desktop/Tutorial/Step1/ tree -L 2
.
├── CMakeLists.txt
├── TutorialConfig.h.in
├── build
│?? ├── CMakeCache.txt
│?? ├── CMakeFiles
│?? ├── Makefile
│?? ├── TutorialConfig.h
│?? └── cmake_install.cmake
└── tutorial.cxx 

以后的项目讲解中将全部使用外部构建。

下面是我专门为讲解一些基本指令编写的代码

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
message(STATUS ${PROJECT_NAME})
message(STATUS ${PROJECT_SOURCE_DIR})
message(STATUS ${PROJECT_BINARY_DIR})
message(STATUS ${Tutorial_SOURCE_DIR})
message(STATUS ${Tutorial_BINARY_DIR})
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

# add the executable
add_executable(Tutorial tutorial.cxx) 
  • 首先看message用法 message类似于一个向控制台输出日志的工具,但是功能又稍微强大一些,在一些模式下能够终止程序构建。
message([<mode>] "message to display" ...) 

mode有以下几个模式

模式 作用
(none) Important information
STATUS Incidental information
WARNING CMake Warning, continue processing
AUTHOR_WARNING CMake Warning (dev), continue processing
SEND_ERROR CMake Error, continue processing,but skip generation
FATAL_ERROR CMake Error, stop processing and generation
DEPRECATION CMake Deprecation Error or Warning if variable CMAKE_ERROR_DEPRECATED or CMAKE_WARN_DEPRECATED is enabled, respectively, else no message.

STATUS我们经常用到。

  • 来看第一行:
cmake_minimum_required (VERSION 2.6) 

为一个项目设置cmake的最低要求版本,并更新策略设置以匹配给定的版本(策略设置我永远也不会讲了)。无论是构建项目还是构建库,都需要这个命令。 它的语法是这样的

cmake_minimum_required(VERSION major.minor[.patch[.tweak]]
                       [FATAL_ERROR]) 

版本号必须指定主次代号,后边的可选,请忽略可选项[FATAL_ERROR],完全没用。

假如你指定的版本号大于你安装的cmake版本,将会停止构建并抛出一个错误:

CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
  CMake 3.11 or higher is required.  You are running version 3.10.2

-- Configuring incomplete, errors occurred! 

cmake_minimum_required必须在项目根目录下的最开始调用,也就是project()之前。在function()中调用该指令也可以,作用域将局限在函数内,但是必须以不影响全局使用为前提

  • 来看第二行
project (Tutorial) 

指定项目的名称为Tutorial,构建项目必须使用这个命令,构建库可以不指定。文档如下:

project(<PROJECT-NAME> [LANGUAGES] [<language-name>...])
project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [LANGUAGES <language-name>...]) 

设置项目名称并将该名称存储在PROJECT_NAME变量中。同时也指定了四个变量:

PROJECT_SOURCE_DIR, <PROJECT-NAME>_SOURCE_DIR
PROJECT_BINARY_DIR, <PROJECT-NAME>_BINARY_DIR 

但是我们一般只使用前一个,这样更容易更改。在上边的代码中我们用message输出了这些变量的信息,执行构建命令后日志输出:

-- Tutorial
-- /Users/saka/Desktop/Tutorial/Step1
-- /Users/saka/Desktop/Tutorial/Step1/build
-- /Users/saka/Desktop/Tutorial/Step1
-- /Users/saka/Desktop/Tutorial/Step1/build 

可以看到这几个变量确实输出了正确的值。

我们也可以在指定项目名称时直接指定版本号,假如没有指定,则版本号为空。 版本号存储在下边几个变量中:

PROJECT_VERSION, <PROJECT-NAME>_VERSION
PROJECT_VERSION_MAJOR, <PROJECT-NAME>_VERSION_MAJOR
PROJECT_VERSION_MINOR, <PROJECT-NAME>_VERSION_MINOR
PROJECT_VERSION_PATCH, <PROJECT-NAME>_VERSION_PATCH
PROJECT_VERSION_TWEAK, <PROJECT-NAME>_VERSION_TWEAK 

通常我们推荐使用前一个。现在测试一下,在CMakeLists.txt文件中修改代码:

project (Tutorial
    VERSION 1.2.3
    DESCRIPTION "this is description"
    LANGUAGES CXX)
message(STATUS ${PROJECT_VERSION})
message(STATUS ${PROJECT_VERSION_MAJOR})
message(STATUS ${PROJECT_VERSION_MINOR})
message(STATUS ${PROJECT_VERSION_PATCH})
message(STATUS ${PROJECT_VERSION_TWEAK})
message(STATUS ${PROJECT_DESCRIPTION}) 

输出日志如下:

-- 1.2.3
-- 1
-- 2
-- 3
-- 
-- this is description 

在这设置版本号和用set设置版本号效果一样,取最后一次设置的值。由于我们没有指定tweak版本,所以为空,同时看到description被存储到PROJECT_DESCRIPTION这个变量中了。

可以通过设置LANGUAGES来指定编程语言是C、CXX(即c++)或者Fortran等,如果没有设置此项,默认启用C和CXX。设置为NONE,或者只写LANGUAGES关键字而不写具体源语言,可以跳过启用任何语言。一般都是用cmake来编译c或者c++程序,所以用默认的就可以了。

  • 来看重要的一行configure_file

该命令的作用是复制文件到另一个地方并修改文件内容。语法如下:

configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])