前言
- 本书资源:
- 感想:也只是了解各大概,一些更进一步的功能需要不停根据项目进行学习。
第三章 初试cmake
3.1. 源码
- 主要包括
main.c
和CMakeLists.txt
main.c
#include <stdio.h>
int main()
{
printf("Hello World from t1 Main!\n");
return 0;
}
CMakeLists.txt
PROJECT (HELLO)
SET(SRC_LIST "main.c")
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
3.2. 基本操作流程
- 第一步,当前文件夹下执行
cmake .
,生成 Makefile 文件。- 这样做是内部构建。
- 第二步,执行
make
。 - 第三步:通过
./hello
运行程序。
3.3. CMakeLists.txt 文件的解释
PROJECT(projectname [CXX] [C] [Java])
:指定工程名称,还可以指定语言。- 隐形定义了两个变量
<projectname>_BINARY_DIR
、<projectname>_SOURCE_DIR
,指的都是工程所路径(猜测就是root CMakeLists.txt 所在路径)。 - cmake 预定义了两个变量
PROJECT_BINARY_DIR
和PROJECT_SOURCE_DIR
- 内部编译时与上面两个变量的功能相同。
- 外部编译时表示的是编译目录,比如构建的
build
目录。
- 隐形定义了两个变量
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
:显示定义变量,如果要使用VAR需要通过${}
来包裹。- 可以指定单个变量,也可以指定list。
SET(SRC_LIST main.c)
SET(SRC_LIST main.c t1.c t2.c)
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
:终端输出用于定义的信息。- 包括三个类型:
SEND_ERROR
只输出错误,生成过程被跳过STATUS
输出前缀为-
的信息FATAL_ERROR
立即终止所有cmake过程
- 包括三个类型:
ADD_EXECUTABLE(hello ${SRC_LIST})
:生成一个名为hello
的可执行文件
3.4. 基本语法规则
- 变量使用
${}
的方式取值,但是在IF
控制语句中是直接使用变量名的。 - 指令的基本格式是
指令(参数1 参数2 ...)
,参数使用括弧括起,参数之间使用空格或分号分开。 - 指令是大小写无关,参数和变量是大小写相关。
- 如果文件名中有空格,那必须用双引号,如
set(SRC_LIST "fu nc.c")
- 可以省略源文件的后缀,如
ADD_EXECUTABLE(t1 main)
就会自动寻找main.c/main.cpp
。- 不推荐使用
3.5. 内部构建与外部构建
- 猜测内部构建就是在CMakeLists文件所在路径执行cmake命令。
- 推荐使用外部构建,即建立单独的文件夹,然后在里面执行
cmake
命令。PROJECT_BINARY_DIR
在内部编译中与PROJECT_SOURCE_DIR
相同,如果是外部编译则指的是外部编译坐在目录,比如build
目录。
第四章 更好一点的Hello World
4.1. 源码
- 项目文件结构如下
project
|_ src
| |_ main.c
| |_ CMakeLists.txt
|_ doc
| |_ hello.txt
|_ bin
| |_ hello
|_ COPYRIGHT
|_ README
|_ runhello.sh
|_ CMakeLists.txt
main.c
与前一节完全一样
#include <stdio.h>
int main()
{
printf("Hello World from t1 Main!\n");
return 0;
}
- 有想个CMkakeLists.txt文件
// 根目录下 CMake Lists.txt 内容
PROJECT (HELLO)
ADD_SUBDIRECTORY(src bin)
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/test2 )
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/test2)
// src目录下 CMake Lists.txxt 内容
ADD_EXECUTABLE(hello main.c)
INSTALL(TARGETS hello RUNTIME DESTINATION bin)
4.2. 目标以及源码解释
- 目标:
- 将构建后的目标文件放入构建目录的
bin
子目录中 - 最终安装这些文件:将
hello
二进制与runshello.sh
安装到/usr/bin
,将doc目录的内容以及 COPYRIGHT/README 安装到当前项目路径下。
- 将构建后的目标文件放入构建目录的
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
- 作用:向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
EXCLUDE_FROM_ALL
参数的含义是将这个目录从编译过程中排除,例如工程example
,一般会在工程建立完成后单独构建。- 上面的例子中
ADD_SUBDIRECTORY(src bin)
将src
子目录加入工程,并制定编译输出(包括中间结果)路径为bin
目录。- 如果不指定
bin
,那么默认放在src
目录中。
- 如果不指定
- 可以通过
SET
指令修改EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
变量
来指定最终的目标二进制的位置(不包含中间文件)。
4.3. INSTALL 命令详解
- INSTALL 命令的作用:安装,猜测就是把目标文件、文件夹放到指定位置。
- 基本流程:就是先
cmake ..
、make
,最后再加上一步make install
。
- 基本流程:就是先
- 常用变量
CMAKE_INSTALL_PREFIX
- 类似于 configure 脚本的
--prefix
- 配合INSTALL命令中的
DESTINATION
构建安装路径 - 默认定义是
/usr/local
- 使用的时候就是
-DCMAKE_INSTALL_PREFIX=/usr
- 类似于 configure 脚本的
- INSTALL 命令分类
- TARGETS:目标文件,如二进制、动态库、静态库
- FILES:普通文件
- PROGRAMS:脚本
- DIRECTORY:目录
- TARGETS
TARGETS
后面跟的就是通过ADD_EXECUTABLE
和ADD_LIBRARY
定义的目标文件,包括二进制可执行文件、动态库、静态库。ARCHIVE
特指静态库,LIBRARY
特指动态库,RUNTIME
特指可执行目标二进制。DESTINATION
定义了安装路径。如果以/
开头表示绝对路径,此时CMAKE_INSTALL_PREFIX
无效;如果是相对路径,那么最终安装路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
- TARGETS 命令举例
- 将二进制myrun安装到
${CMAKE_INSTALL_PREFIX}/bin
- 将动态库mylib安装到
${CMAKE_INSTALL_PREFIX}/lib
- 将静态库mystaticlib安装到
${CMAKE_INSTALL_PREFIX}/libstatic
- 将二进制myrun安装到
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)
- FILE:普通文件安装
- 安装一般文件,并设置访问权限。
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
- PROGRAMS:非目标文件的可执行程序安装(例如脚本)
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
- DIRECTORY:目录安装
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
第五章 静态库与动态库构建
- 目标:
- 建立一个静态库和动态库,提供
HelloFun
函数供其他程序编程使用,HelloFunc
向终端输出 Hello World 字符串。 - 安装头文件与共享库。
- 建立一个静态库和动态库,提供
5.1. 源码
- 建立 lib 文件夹,新建两个原文件,
hello.c/hello.h
// main.c
#include "hello.h"
void HelloFunc()
{
printf("Hello World\n");
}
// main.h
#ifndef HELLO_H
#define HELLO_H
#include <stdio.h>
void HelloFunc();
#endif
- CMakeLists.txt 有两个,分别在根目录和lib文件夹中
# 根目录
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
# lib 文件夹
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
5.2. ADD_LIBRARY & SET_TARGET_PROPERTIES 解析
ADD_LIBRARY
指令基本形式请参考下面代码- 假设libname是
hello
,则默认获取的就是libhello.so/libhello.a
文件。 SHARED
指动态库(.so
文件),STATIC
指静态库(.a
文件),MODULE
使用dyld系统有效(如果不支持,则被当做动态库)。- 限制:libname 不能重复,因此如果想为一个库同时建立动态库与静态库,需要使用两个
ADD_LIBRARY
,且两个名称必须不一样,则不能同时获取libhello.so/libhello.a
文件。
- 假设libname是
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
SET_TARGET_PROPERTIES
指令- 同时获取
libhello.so/libhello.a
文件。 - 指定动态库版本。
- 同时获取
// 命令形式
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)、
// 同时生成动态库与静态库
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
// 指定API版本与动态库版本
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
第六章 如何使用外部共享库和头文件
- 目标:编写程序使用上一届构建的共享库。
6.1. 源码
- 构建
src
目录 ,包含main.c
#include <hello.h>
int main(void)
{
HelloFunc();
return 0;
}
- 有两个CMakeLists.txt,分别在根目录和src目录下
# 根目录
PROJECT(HELLO)
ADD_SUBDIRECTORY(src)
# src 目录
INCLUDE_DIRECTORIES(/usr/include/hello)
TARGET_LINK_LIBRARIES(main hello)
ADD_EXECUTABLE(main main.c)
6.2. INCLUDE_DIRECTORIES
& LINK_DIRECTORIES
& TARGET_LINK_LIBRARIES
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
:- 作用:向工程添加多个特定的头文件搜索路径。
- 路径之间用空格分隔。
- 如果路径包含空格,可以通过双引号括起来。
CMAKE_INCLUDE_DIRECTORIES_BEFORE
:通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面。- 通过
AFTER
或者BEFORE
参数,也可以控制是追加还是置前。
LINK_DIRECTORIES(directory1 directory2 ...)
- 添加非标准的共享库搜索路径。
- 在工程内部同时存在共享库和可执行二进制,在编译时就需要指定一下这些共享库的路径
TARGET_LINK_LIBRARIES
- 为 target 添加需要链接的共享库,可以是可执行文件,也可以是共享库。
TARGET_LINK_LIBRARIES(target library1
<debug | optimized> library2
...)
6.3. 相关环境变量
- 注意,这里是环境变量,而不是cmake变量。
CMAKE_INCLUDE_PATH
和CMAKE_LIBRARY_PATH
- 主要是用来解决以前 autotools 工程中
--extra-include-dir
等参数的支持的。
- 主要是用来解决以前 autotools 工程中
INCLUDE_DIRECTORIES(/usr/include/hello)
等价于:- 新增环境变量
export CMAKE_INCLUDE_PATH=/usr/include/hello
- 以及下面四句,寻找
hello.h
所在路径,并添加到cmake环境中。 - 从
CMAKE_INCLUDE_PATH
以及系统默认头文件目录/usr/include
和/usr/local/include
中寻找。
- 新增环境变量
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORY(${myHeader})
ENDIF(myHeader)
- 类似的,
CMAKE_LIBRARY_PATH
环境变量可与FIND_LIBRARY
配合使用。
第七章 cmake 常用变量和常用环境变量
-
cmake变量引用方法:
- 一般使用
${}
引用,如果在IF等语句中,可以直接使用变量名。
- 一般使用
-
cmake自定义变量的方式
- 隐式定义,例如PROJECT指令会隐式定义
<projectname>_BINARY_DIR
变量。 - 显示定义,通过SET指令,如
SET(HELLO_SRC main.c)
- 隐式定义,例如PROJECT指令会隐式定义
-
cmake 常用变量
CMAKE_BINARY_DIR
/PROJECT_BINARY_DIR
/<projectname>_BINARY_DIR
- 这三个变量指代的内容一致。
- 如果是内部构建,则表示项目顶层目录;如果是外部构建,指的是工程编译发生的目录。
PROJECT_BINARY_DIR
稍有区别,但此处可理解为是一致的。
CMAKE_SOURCE_DIR
/PROJECT_SOURCE_DIR
/<projectname>_SOURCE_DIR
- 这三个变量内容是一致的。
- 不管什么构建方式,都指的是工程顶层目录。
CMAKE_CURRENT_SOURCE_DIR
:当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。CMAKE_CURRRENT_BINARY_DIR
- 如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-ofsource 编译,他指的是 target 编译目录。
ADD_SUBDIRECTORY(src bin)
可以更改这个变量的值。SET(EXECUTABLE_OUTPUT_PATH <新路径>)
并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径
CMAKE_CURRENT_LIST_FILE
:输出调用这个变量的 CMakeLists.txt 的完整路径CMAKE_CURRENT_LIST_LINE
:输出这个变量所在的行CMAKE_MODULE_PATH
:定义自己的 cmake 模块所在的路径EXECUTABLE_OUTPUT_PATH
/LIBRARY_OUTPUT_PATH
:分别用来重新定义最终结果的存放目录PROJECT_NAME
:返回通过 PROJECT 指令定义的项目名称
-
cmake 调用环境变量的方式
- 使用:
$ENV{NAME}
- 设置:
SET(ENV{变量名} 值)
- 使用:
-
还有一些,直接看网页吧。