cmake practice学习记录

1. 初识cmake

  1. cmake特点:开源,跨平台,管理大型项目,简化编译构建过程,可扩展。
  2. cmake缺点:并不那么简单;cmake编写过程实际上是编程过程,每个目录都需要有CMakeLists.txt;和已有体系配合并不理想。
  3. 建议:只有有需求,实践才能学好,读过几天后就忘记了;工程只有几个文件,建议直接编写MakeFile;如果使用的语言不是C/C++/Java,不要使用cmake。

2. 安装cmake

3. 初试cmake(hello world)

  1. Cmake自动生成MakeFile。
# 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})

###指令1
PROJECT(projectname [CXX] [C] [Java])
# 1.隐式定义两个cmake变量:_BINARY_DIR 和_SOURCE_DIR
# 2.采用内部编译,两个变量所指目录皆为工程所在目录
# 3.系统自动定义PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR,其和1中变量一样;
#   采用外部编译,则目录为编译所在文件夹。

###指令2
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
# 1.显式的定义变量,也可以定义多个源文件如SET(SRC_LIST main.c t1.c t2.c)

###指令3
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display")
# 1.用于向终端输出用户定义信息,包含3种类型:SEND_ERROR(产生错误 生成过程被跳过)   
#    STATUS(输出前缀为_的信息)  FATAL_ERROR(立即终止所有cmake过程)

###指令4
ADD_EXECUTABLE(HELLO ${SRC_LIST})
# 1.定义此工程生成一个名为hello的可执行文件,相关源文件就是变量SRC_LIST中的源文件列表
  1. 变量使用${}的方法进行取值;但是在IF语句中是直接使用变量名。
  2. 指令(参数1 参数2);指令的参数用()扩起,参数之间使用空格和分号分开。
  3. 指令大小写无关;参数和变量大小写相关;推荐全部使用大写指令。
  4. 运行make clean可对构建结果进行清理。
  5. 内部构建和外部构建:内部构建会生成一堆乱七八糟的内容;新建一个编译目录,所有文件都会生成在编译目录,这就是外部构建。

4.更好的hello word

  1. 外部构建进行生成。创建编译目录bulid;在build目录中执行cmake … 即编译父目录。编译结果都保存在build目录中。
# 源码文件夹中的CMakeLists.txt示例
ADD_EXECUTABLE(hello main.c)
# 工程中CMakeList.txt示例
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

###指令1
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
# 1.用于向当前工程添加存放源文件的子目录,并指定中间二进制或者目标二进制存放的位置。
# 2.EXCLUDE_FROM_ALL将这个目录从编译过程中排除。
# 3.编译目录指定为build/bin;如果不指定编译结果将保存在build/src中;前面有build是因为在build目录中进行的编译。
# 4.SUBDIRS指令和ADD_SUBDIRECTORY类似,但不推荐使用;其可以一次添加多个子目录,如:SUBDIRS(dir1 dir2 ...)
# 5.无论是SUBDIRS还是ADD_SUBDIRECTORY指令(无论是否指定编译输出目录),都可以通过SET指令重新定义EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH变量---来指定最终目标二进制的位置。
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# 1. 将输出目录和编译目录关联起来。(见上段代码指令1的第五条解释)
# 2. 两条命令的位置:是写在工程的CMakeLists.txt还是写在src目录的CMakeLists.txt?
#    原则是ADD_EXECUTABLE或者ADD_LIBRARY在哪定义,此命令就在哪里加入。
  1. 安装方式分为两种:代码编译后直接make install;打包时指定目录安装。
# MakeFile样式:
DESTDIR=
install:
	mkdir -p ${DESTDIR}/usr/bin
	install -m 755 hello ${DESTDIR}/usr/bin
# 1.可以通过make install安装在/usr/bin目录。
# 2.也可以通过make install DESTDIR=/tmp/test安装在/tmp/test/usr/bin目录,打包时常用此方式。 
  1. 复杂一点还需要定义PREFIX。
DESTDIR=
PREFIX=/usr
install:
	mkdir -p ${DESTDIR}/${PREFIX}/bin
	install -m 755 hello ${DESTDIR}/${PREFIX}/bin
# 1. 编译时使用命令cmake -D CMAKE_INSTALL_PREFIX=/usr 
  1. INSTALL指令
    目标文件安装:
# INSTALL指令解析(目标文件安装)
INSTALL(TARGETS targets...
	[ 
		[ARCHIVE|LIBRARY|RUNTIME]
		[DESTINATION <dir>]
		[PERMISSIONS permissions ...]
		[CONFIGURATIONS [Debug|Release|...]]
		[COMPONENT <component>]
		[OPTIONAL]
	]
	[...]
)
# 1.TARGETS后面跟的就是通过ADD_EXECUTABLE或者ADD_LIBRARY定义的目标文件
#    ARCHIVE:静态库  LIBRARY:动态库  RUNTIME:可执行目标二进制
# 2.DESTINATION定义了安装路径,如果以/开头,指绝对路径,此时CMAKE_INSTALL_PREFIX无效
#   如果希望使用CMAKE_INSTALL_PREFIX,需要写成相对路径
# 例子
INSTALL(TARGETS myrun mylib mystaticlib
	RUNTIME DESTNATION bin
	LIBRARY DESTNATION lib
	ARCHIVE DESTNATION libstatic
)
# 1.将可执行二进制myrun安装到${CMAKE_INSTALL_PREFIX}/bin目录 
# 2.将动态库mylib安装到${CMAKE_INSTALL_PREFIX}/lib目录 
# 3.将静态库mystaticlib安装到${CMAKE_INSTALL_PREFIX}/libstatic目录 

普通文件安装:

# 指令解析(普通文件安装)
INSTALL(FILES files ...
	[DESTINATION <dir>]
	[PERMISSIONS permissions...]
	[CONFIGURATIONS [Debug|Release|...] ]
	[COMPONENT <component>]
	[RENAME <name>]
	[OPTIONAL]
)
# 1.用于安装一般文件,并可指定访问权限,文件名时此指令所在路径下的相对路径。
#   默认权限为OWNER_WRITE, OWNER_READ, GROUP_READ, WORLD_READ 即644权限

非目标文件可执行程序安装(比如脚本类):

# 指令解析(非目标可执行程序)
INSTALL(PROGRAMS files...
	[DESTINATION <dir>]
	[PERMISSIONS permissions...]
	[CONFIGURATIONS [Debug|Release|...] ]
	[COMPONENT <component>]
	[RENAME <name>]
	[OPTIONAL]	
)
# 1.跟上面普通文件安装方法一样,唯一不同的是默认安装后的权限:
#	OWNER_EXECUTE, GROUP_EXECUTE,WORLD_EXECUTE 即755权限

目录安装:

# 目录安装:
INSTALL(DIRECTORY dirs...
	[DESTINATION <dir>]
	[FILE_PERMISSIONS permissions...]
	[DIRECTORY_PERMISSIONS permisssions...]
	[USE_SOURCE_PERMISSIONS]
	[CONFIGURATIONS [Debug|Release|...] ]
	[COMPONENT <component>]
	[PATTERN <pattern> | REGEX <regex>]
	[EXCLUDE]
	[PERMISSIONS permissions...]
	[...]
)
# 1.DIRECORY后面连接的是所在Source目录的相对路径,但注意abc和abc/差别巨大:
#	目录名不以/结尾,这个目录将被安装为目标路径下的abc
#	目录名以/结尾,代表将这个目录的内容安装到目标路径,但是不包括这个目录本身
# 2.PATTERN用于使用正则表达式进行过滤。
# 示例
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
	PATTERN "CSV" EXCLUDE
	PATTERN "scripts/*"
	PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ
)
# 1.将icons目录和scripts/目录中的内容都安装到/share/myproj
# 2.安装内容不包含目录名为CSV的目录,并对文件指定权限
  1. 修改hello world支持安装

  2. CMAKE_INSTALL_PREFIX默认安装目录是/usr/local

5.静态库和动态库构建

cmake practice学习记录_第1张图片

  1. 在build目录进行编译,就会得到一个libhello.so的共享库。
# ADD_LIBRARY指令解析
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL])
# 1.libname无需写全,只需填入hello,即可生成libhello.so文件。
# 2.SHARED:动态库 STATIC:静态库 MODULE:在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。
# 3.EXCLUDE_FROM_ALL参数意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。
  1. 如果需要同时构建静态库,再添加一行静态库指令ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})无用,因为hello作为一个target是不能同名的,需要使用指令SET_TARGET_PROPERTIES指令。
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static STATIC ${LIBHELLO_SRC})
# SET_TARGET_PROPERTIES指令解析
SET_TARGET_PROPERTIES(target1 target2...
	PROPERTIES prop1 value1 prop2 value2)
# 1.这条指令可用于设置输出名称,对于动态库,还可以用来指定动态库版本和API版本。
# 2.据说在构建libhello.a时,会清理掉其它使用这个名字的库,即libhello.so。(测试时并未遇到这种现象) 如果遇到的话:
#	使用指令:SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
#	和指令:SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)来解决。
  1. 与SET_TARGET_PROPERTIES对应的指令是GET_TARGET_PROPERTY(VAR target property)
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static STATIC ${LIBHELLO_SRC})
GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE(STATUS "this is the hello_static OUTPUT_NAME:"${OUTPUT_VALUE})
# GET_TARGET_PROPERTY指令解析:
1. 与SET_TARGET_PROPERTIES指令对应,获取属性。
  1. 动态库版本号。
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
# 指令解析
# 1. VERSION指动态库版本,SOVERSION指API版本。
  1. 安装共享库和头文件:上面例子,需要将libhello.a libhello.so 以及hello.h安装到系统目录,才能真正让其他人开发使用。
INSTALL(TARGET hello hello_static
	LIBRARY DESTINATION lib
	ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
# 1.注意静态库需要使用ARCHIVE关键字。
# 2.通过
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install
# 就可以将头文件和共享库安装到系统目录/usr/lib 和 /usr/include/hello中。

6. 使用外部共享库和头文件

  1. 上一节完成了libhello动态库的构建以及安装,本节需要使用上节编写的动态库。
  2. 如果上述动态库未安装到标准头文件路径和动态库路径的话,会报找不到hello.h。
  3. 可使用INCLUDE_DIRECTORIES添加头文件搜索路径。
INCLUDE_DIRECTORIES([AFTER | BEFORE] [SYSTEM] dir1 dir2 ...)
# 指令解析:
# 1.本指令用来向工程添加多个特定头文件的搜索路径;路径之间用空格分开;如果路径之间包含了空格,可以使用双引号
#	将它括起来;默认是追加到当前头文件搜索路径的后面;
#	可使用CMAKE_INCLUDE_DIRECTORIES_BEFORE=on控制追加在前面。
  1. 使用LINK_DIRECTORIES添加共享库路径 TARGET_LINK_LIBRARIES用来为Target添加需要的共享库。
LINK_DIRECTORIES(directory1 directory2 ...)
TARGET_LINK_LIBRARIES(target library1 <debug|optiimized> library2 ...)
# 指令解析:
# 1.TARGET_LINK_LIBRARIES(main hello)也可以写成
#	TARGET_LINK_LIBRARIES(main libhello.so)
  1. 特殊的环境变量CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH。
# 这两个变量需要用bash中export命令 或者csh中set命令进行设置。
CMAKE_INCLUDE_PATH=/home/include/... 
  1. 为了程序更智能一些可使用下面指令修改CMakeLists.txt
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF
# 1.FIND_PATH用来搜索指定路径中的文件名。
# 2.FIND_LIBRARY用来搜索CMAKE_LIBRARY_PATH中的动态库的路径。

7.cmake常用变量和常用环境变量

  1. cmake使用 进 行 变 量 引 用 ; 再 I F 等 语 句 中 , 是 直 接 使 用 变 量 名 而 不 是 通 过 {}进行变量引用;再IF等语句中,是直接使用变量名而不是通过 IF使{}取值。
  2. cmake主要有隐式定义和显示定义两种:显示定义:前面的PROJECT指令,会隐式的定义_BINARY_DIR和_SOURCE_DIR两个变量;隐式定义:使用SET指令,如SET(HELLO_SRC main.c)
  3. 常用变量:
    cmake practice学习记录_第2张图片
    cmake practice学习记录_第3张图片
  4. 环境变量调用方式:$ENV{NAME}.
  5. 环境变量设置方式:$ENV{NAME}
  6. 主要开关选项:
    cmake practice学习记录_第4张图片

8.cmake常用指令

你可能感兴趣的:(其他,c++)