
全栈工程师开发手册 (作者:栾鹏)
架构系列文章
基本使用
一、 基本使用
安装:下载二进制包后可直接解压使用
从源码安装则执行命令:./bootstrap; make; make install——尝试执行bootstrap失败
使用:cmake dir_path,生成工程文件或makefile文件
二、 概念
out-of-source build,与in-source build相对,即将编译输出文件与源文件放到不同目录中;
三、 基本结构
1,依赖CMakeLists.txt文件,项目主目标一个,主目录中可指定包含的子目录;
2,在项目CMakeLists.txt中使用project指定项目名称,add_subdirectory添加子目录
3,子目录CMakeLists.txt将从父目录CMakeLists.txt继承设置(TBD,待检验)
cmake基本语法
Cmake的输入是在源码目录下的CMakeLists.txt文件。这个文件可以用include或者 add_subdirectory 命令增加入其它的输入文件。
语法
CMakeList.txt文件是由注释、命令和空白字符组成。
注释是由 # 开始,到行结尾。
命令是由:命令名、(、空格分隔的参数、)组成。
例如:command (args….)
上面的command可以是一个命令名;或者是一个宏;也可以是一个函数名。
args是以空格分隔的参数例表(如果参数中包含空格,则要加双引号)
除了用于分隔参数的空白字符(空格、换行号、tabs)都是被忽略不计的。任何包含在双引号中的字符都做为一个参数。一个反斜杠用于转换码。
命令名是大小写不敏感的。
字符串(string)和字符串列表(lists)
CMake的基本数据类型是字符串(string)。CMake也支持由字符串组成的字符串列表。字符串列表可以由;或空格分隔的组成。例如:下面设置变量var是等效的。
set(var a;b;c)
set(var a b c)
字符串列表可以用 foreach命令叠代(iterated)或list命令操作。
变量
CMake支持简单的变量:字符串或字符串列表。用${VAR} 语法得到变量的引用。
可以用一个set命令把一个字符串列表设置为一个变量,然后把这个变量传递给需要传递多参数的函数。例如:
set(Foo a b c)
command(${Foo})
上面两行等效 command(a b c)
如果你想传把一个字符串列表做为一个单独的参数传递给函数,用双引号包含它。例如:
Command(“${Foo}”)
等效于:command(“a b c”)
环境变量:
用$ENV{VAR}得到环境变量的引用
设置环境变量:
Set(ENV{VAR} /home)
程序流控制
CMake提供三种程序流控制结构:
1、 条件声明:if
# some_command will be called if the variable's value is not:
# empty, 0, N, NO, OFF, FALSE, NOTFOUND, or -NOTFOUND.
if(var)
some_command(...)
endif(var)
2、 循环控制:foreach 和 while
set(VAR a b c)
# loop over a, b,c with the variable f
foreach(f ${VAR})
message(${f})
endforeach(f)
3、 程序定义:宏或函数(函数在CMake2.6以后的版本才支持)。函数建立本地范围内的变量,宏用于全局范围内。
# define a macro hello
macro(hello MESSAGE)
message(${MESSAGE})
endmacro(hello)
# call the macro with the string "hello world"
hello("hello world")
# define a function hello
function(hello MESSAGE)
message(${MESSAGE})
endfunction(hello)
函数可以返回,可以用 return()命令返回。如果要从函数中返回值,只能通过参数返回:
set命令中 PARENT_SCOPE表示传递给函数调用者所拥有的变量
引号、字符串和escapes
一个用双引号包含的字符串,是这个字符串的字面含义。一个字符串能包含多行。例如:
set (MY_STRING "this is a string with a
newline in
it")
也能在字符串中用转义字符
set (VAR "
hello
world
")
message ( "\${VAR} = ${VAR}")
# prints out
${VAR} =
hello
world
标准C的转义字符被支持
message("\n\thello world")
# prints out
hello world
message(hell"o") -> prints hell"o"
message(hell"o") -> prints hell"o"
message(hell\"o\") -> prints hell"o"
引号必须是匹配的
message(hell"o) -> produces this error:
Parse error. Function missing ending ")".
Instead found unterminated string with text "o)
".
message(hell\"o) -> prints hell"o
正则表达式
一些CMake命令(如if和 string),能使用正则表达式或使用正则表达式作为参数。一个简单的形式,一个正则表达式用于在一个序列的字符串中精确查找一个字符。然而在大多时候,一个精确查找是不知道的或者只是匹配最前或者最后字符。所以这里用正则表达式进行不同的转换。Cmake标准是可被描述的。这个描述是基于开源的正则表达式类(Texas Instruments)。
^ 匹配一行或一字符串开头
$匹配一行或一字符串结尾
.匹配单一字符或一个新行
[ ]匹配括号中的任一字符
[^ ] 匹配不在括号内的任一字符
[-] 匹配指定范围内的字符
* 匹配0次或多次
+ 匹配一次或多次
? 匹配0次或一次
()保存匹配的表达式并用随后的替换它
CMAKE进阶
内部变量
CMAKE_C_COMPILER:指定C编译器
CMAKE_CXX_COMPILER:
CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项
EXECUTABLE_OUTPUT_PATH:可执行文件的存放路径
LIBRARY_OUTPUT_PATH:库文件路径
CMAKE_BUILD_TYPE::build 类型(Debug, Release, …),CMAKE_BUILD_TYPE=Debug
BUILD_SHARED_LIBS:Switch between shared and static libraries
内置变量的使用:
在CMakeLists.txt中指定,使用set
cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF
命令
project (HELLO) #指定项目名称,生成的VC项目的名称;
使用${HELLO_SOURCE_DIR}表示项目根目录
include_directories:指定头文件的搜索路径,相当于指定gcc的-I参数
include_directories (${HELLO_SOURCE_DIR}/Hello) #增加Hello为include目录
link_directories:动态链接库或静态链接库的搜索路径,相当于gcc的-L参数
link_directories (${HELLO_BINARY_DIR}/Hello) #增加Hello为link目录
add_subdirectory:包含子目录
add_subdirectory (Hello)
add_executable:编译可执行程序,指定编译,好像也可以添加.o文件
add_executable (helloDemo demo.cxx demo_b.cxx) #将cxx编译成可执行文件——
add_definitions:添加编译参数
add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义;
add_definitions( “-Wall -ansi –pedantic –g”)
target_link_libraries:添加链接库,相同于指定-l参数
target_link_libraries(demo Hello) #将可执行文件与Hello连接成最终文件demo
add_library:
add_library(Hello hello.cxx) #将hello.cxx编译成静态库如libHello.a
add_custom_target:
message( status|fatal_error, “message”):
set_target_properties( … ): lots of properties… OUTPUT_NAME, VERSION, …
link_libraries( lib1 lib2 …): All targets link with the same set of libs
实例:
先制作makefile文件 以便以后运行
CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.10)
project(cpptensorflow)
set(CMAKE_CXX_STANDARD 11)
link_directories(/home/lp/projects/safety/tensorflow_c/tensorflow_cpp/tensorflow)
include_directories(
/home/lp/projects/safety/tensorflow_c/tensorflow-master/tensorflow
/home/lp/projects/safety/tensorflow_c/tensorflow-master/tensorflow/bazel-genfiles
/home/lp/projects/safety/tensorflow_c/tensorflow-master/tensorflow/bazel-bin/tensorflow
/home/lp/projects/safety/tensorflow_c/eigen3
)
add_executable(cpptensorflow main.cpp ann_model_loader.h model_loader_base.h ann_model_loader.cpp)
target_link_libraries(cpptensorflow tensorflow_cc tensorflow_framework)
其中 link_directories为指定优先搜索库的路径,也就是.so文件的路径 默认搜索 /usr/local/lib目录,
该目录下有很多.so目录 include_directories为包含的unclude目录.
add_executable参数为要生成的可执行文件的文件名, 要编译的cpp/h文件
target_link_libraries为在lib目录下(包含系统lib目录/usr/local/lib和手动添加目录link_directories,)要包含的.so文件名.其中去掉文件名前面的lib字符串 当多个库存在依赖时,依赖库要写在后面 如果你添加了目标库,但是没有添加目标库的依赖库,就会报错,这个时候你需要将缺少的库也添加到target_link_libraries里面
注意:库的名称是.so文件的文件名去掉前面的lib字符串
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
编写 CMake 配置文件 CMakeLists.txt 。
执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile, 其中, PATH 是 CMakeLists.txt 所在的目录。
其中ccmake 和 cmake 的区别在于前者提供了一个交互式的界面。
使用 make 命令进行编译。
在CMakeLists.txt目录下执行
mkdir build
cd build
cmake ..
make
这样就可以生成可执行文件了
cmake 添加头文件目录,链接动态、静态库
1, 添加头文件目录INCLUDE_DIRECTORIES
语法:
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
例如:
include_directories(../../../thirdparty/comm/include)
它相当于g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用。
2, 添加需要链接的库文件目录LINK_DIRECTORIES
语法:
link_directories(directory1 directory2 ...)
它相当于g++命令的-L选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH的路径的作用。
示例:
link_directories("/home/server/third/lib")
3, 查找库所在目录FIND_LIBRARY
A short-hand signature is:
find_library (<VAR> name1 [path1 path2 ...])
The general signature is:
find_library (
<VAR>
name | NAMES name1 [name2 ...] [NAMES_PER_DIR]
[HINTS path1 [path2 ... ENV var]]
[PATHS path1 [path2 ... ENV var]]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[DOC "cache documentation string"]
[NO_DEFAULT_PATH]
[NO_CMAKE_ENVIRONMENT_PATH]
[NO_CMAKE_PATH]
[NO_SYSTEM_ENVIRONMENT_PATH]
[NO_CMAKE_SYSTEM_PATH]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH]
)
示例:
FIND_LIBRARY(RUNTIME_LIB rt /usr/lib /usr/local/lib NO_DEFAULT_PATH)
cmake会在目录中查找,如果所有目录中都没有,值RUNTIME_LIB就会被赋为NO_DEFAULT_PATH
4, 添加需要链接的库文件路径LINK_LIBRARIES
语法:
link_libraries(library1 <debug | optimized> library2 ...)
# 直接是全路径
link_libraries(“/home/server/third/lib/libcommon.a”)
# 下面的例子,只有库名,cmake会自动去所包含的目录搜索
link_libraries(iconv)
# 传入变量
link_libraries(${RUNTIME_LIB})
# 也可以链接多个
link_libraries("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
可以链接一个,也可以多个,中间使用空格分隔.
5, 设置要链接的库文件的名称TARGET_LINK_LIBRARIES
语法:
target_link_libraries(<target> [item1 [item2 [...]]]
[[debug|optimized|general] <item>] ...)
# 以下写法都可以:
target_link_libraries(myProject comm) # 连接libhello.so库,默认优先链接动态库
target_link_libraries(myProject libcomm.a) # 显示指定链接静态库
target_link_libraries(myProject libcomm.so) # 显示指定链接动态库
# 再如:
target_link_libraries(myProject libcomm.so) #这些库名写法都可以。
target_link_libraries(myProject comm)
target_link_libraries(myProject -lcomm)
6,为工程生成目标文件
语法:
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
简单的例子如下:
add_executable(demo
main.cpp
)
7, 最后贴一个完整的例子
cmake_minimum_required (VERSION 2.6)
INCLUDE_DIRECTORIES(../../thirdparty/comm)
FIND_LIBRARY(COMM_LIB comm ../../thirdparty/comm/lib NO_DEFAULT_PATH)
FIND_LIBRARY(RUNTIME_LIB rt /usr/lib /usr/local/lib NO_DEFAULT_PATH)
link_libraries(${COMM_LIB} ${RUNTIME_LIB})
ADD_DEFINITIONS(
-O3 -g -W -Wall
-Wunused-variable -Wunused-parameter -Wunused-function -Wunused
-Wno-deprecated -Woverloaded-virtual -Wwrite-strings
-D__WUR= -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DTIXML_USE_STL
)
add_library(lib_demo
cmd.cpp
global.cpp
md5.cpp
)
link_libraries(lib_demo)
add_executable(demo
main.cpp
)
# link library in static mode
target_link_libraries(demo libuuid.a)
另外,使用cmake生成makefile之后,make edit_cache可以编辑编译选项。