文章目录
- 一、引入
- 二、基本关键字
- 1.PROJECT
- 2.SET
- 3.MESSAGE
- 4.ADD_EXECUTABLE
- 5.ADD_SUBDIRECTORY
- (1)使用
- (2)CMakeLists执行顺序
- (3)输出文件的位置
- 6.INSTALL
- (1)安装文件
- (2)安装非目标文件可执行文件
- (3)安装目录
- (4)安装指令
- 7.ADD_LIBRARY
- 8.SET_TARGET_PROPERTIES
- 三、语法的基本规则
- 四、使用CMake编写一个Hello CMake
- 五、内部构建与外部构建
- 六、让Hello world看起来更像一个工程
- 1.整体结构
- 2.编写两个CMakeLists.txt
- 3.安装HelloWorld
- 七、建立动态库和静态库
- 1.结构
- 2.CMakeLists编写
- 3.静态库与动态库的构建
- (1)动态库与静态库重名构建失败
- (2)解决办法
- (3)动静态库的安装
- 八、使用动态库与静态库
- 1.链接头文件
- 2.链接动态库
一、引入
当多个人要使用不同的语言或者编译器来开发一个项目的时候,最终要输出一个可执行文件或者共享库(dll,so等等),CMake使得所有的操作都是通过编译CMakeLists.txt来实现的。
学习CMake的目的是为了将来处理大型的C/C++项目,可以先看下面的例子参照上面的关键字来进行学习。
二、基本关键字
1.PROJECT
PROJECT:用来指定工程的名字以及支持的语言,支持的语言不填的话,默认是支持所有语言。
PROJECT(HELLO) #默认支持所有语言
PROJECT(HELLO C CXX) #指定支持C和C++
执行该条指令的时候隐式定义了两个CMAKE变量,分别是<projectname>_BINARY_DIR和<orojectname>_BINARY,其中<orojectname>就是项目名,也就是HELLO。这两个变量下面介绍的MESSAGE关键字都是可以直接使用的,在外部编译的部分介绍两者的区别。
2.SET
SET:用于显示的指定变量。
SET(SRC_LIST test.cc) #SRC_LIST变量中就包含了test.cc
SET(SRC_LIST "test.cc") #也可以加双引号
SET(SRC_LIST test1.cc test2.cc) #一个变量中可以包含多个源文件
在SET指令中,如果源文件名中含有空格(比如ma in.cc),则必须加双引号,如果没有加不加都可以。
3.MESSAGE
MESSAGE:向终端输出用户自定义的信息。
主要包含三种信息:
- SEND_ERROR,产生错误,生成过程被跳过。
- STATUS,输出前缀为–的信息。
- FATAL_ERROR,立即终止所有CMake的过程。
MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR}) #此时的输出带有前缀--
4.ADD_EXECUTABLE
ADD_EXECUTABLE:生成可执行文件。
ADD_EXECUTABLE(test ${SRC_LIST}) #将变量SRC_LIST中文件编译成可执行文件test
ADD_EXECUTABLE(test test.cc) #这样写也可以,不使用变量
其中test.cc的后缀可以不写,会自动去找.c或者.cc,但是不推荐这样写。
5.ADD_SUBDIRECTORY
(1)使用
ADD_SUBDIRCTORY:
- 这个指令用来向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置。
- EXCLUDE_FROM_ALL函数是将写的目录从编译中排除,如程序中的example。
ADD_SUBDIRECTORY(src bin) #将src的子目录加入工程,并指定编译结果输出路径为bin目录。
该指令会自动创建一个bin目录,如果不指定则结果会放在build/src即SOURCE_DIR目录。
(2)CMakeLists执行顺序
CMakeLists是逐层进行执行的:
- 首先执行cmake指定路径下的CMakeLists。
- 当在CMakeLists中遇到ADD_SUBDIRECTORY的时候跳转到该关键字指定的路径下执行该路径下的CMakeLists。
- 执行之后返回跳转位置的下一条指令,然后继续按顺序执行。
(3)输出文件的位置
ADD_SUBDIRECTORY可以识别出指定路径中CMakeLists中的ADD_EXECUTABLE生成的可执行文件是一个输出文件,并会将该文件添加到指定的输出文件路径。这个输出文件也可以是一个动态库或者静态库。
6.INSTALL
INSTALL:为安装关键字,他可以用来安装一个文件,也可以用来安装脚本等。
(1)安装文件
安装文件:
INSTALL(FIELS README DESTINATION share/doc/cmake/)
- 其中FILES表示安装文件,README是文件名,DESTINATION后面跟的是安装路径。
- 这个路径可以是一个绝对路径,也可以是一个相对路径。
- 如果是相对路径会使用CMake的一个变量CMAKE_INSTALL_PREFIX,他表示的是/usr/local/。因此我们安装的绝对路径是/usr/local/share/doc/cmake。
(2)安装非目标文件可执行文件
安装脚本:
INSTALL(PROGRAMES runhello.sh DESTINATION bin)
- 其中PROGRAMES表示的是非目标文件的可执行文件,bin还是一个相对路径。
- 此时文件绝对路径为/usr/local/bin
(3)安装目录
安装目录:
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
- 其中DIRECTORY表示目录。
- 注意doc/和doc是有区别的,doc/表示将整个目录安装到对应路径下,二doc表示将目录中的内容安装到对应路径下。
(4)安装指令
注意仅仅cmake是不能够进行安装的,而是在生成makefile之后还需要进行make install操作。才算安装成功。
7.ADD_LIBRARY
ADD_LIBRARY:用来将源文件创建成动态库,即将test.cc编程libtest.so
ADD_LIBRARY(hello SHARED test.cc)
- 其中hello为库名,生成的名字后会加上前缀和后缀,及libhello.so。
- SHARED表示动态库,STATIC表示静态库。
- test.cc:源文件。
8.SET_TARGET_PROPERTIES
SET_TARGET_PROPERTIES:用来设置输出的名称,对于动态库,还可以指定动态库版本和API版本。
这个在动静态库中结合场景具体介绍。
三、语法的基本规则
- 变量使用${}来进行取值,比如上述的ADD_EXECUTABLE中的${SRC_LIST},就是获取该变量中的值,该变量中可以是多个值也可以是一个值。
- 关键字指令的参数是使用空格或者分号来进行隔开的。
- 指令无关大小写,参数和变量有关。
四、使用CMake编写一个Hello CMake
这里主要编写CMakeLists.txt
PROJECT(TEST)
SET(SRC_LIST test.cc)
MESSAGE(STATUS "This is a BINARY dir" ${TEST_BINARY_DIR})
MESSAGE(STATUS "This is a SOURCE dir" ${TEST_SOURCE_DIR})
ADD_EXECUTABLE(test ${SRC_LIST})
假设CMakeLists.txt和test.cc在同一个路径下:
执行:cmake .
可以看到生成了Makefile文件,此时make,就可以完成对test.cc的编译。
五、内部构建与外部构建
上面的例子就是内部构建,而外部构建与之的区别是新建一个build目录,在build目录中进行CMake的操作。我们可以将上面CMake生成的文件进行删除,然后在build中对上一级目录进行CMake。
此时在build目录下执行make,就可以在build下生成可执行文件了。
同时我们可以发现BINARY的路径变为了build路径,而SOURCE没有发生变化。
强烈推荐使用外部构建。
六、让Hello world看起来更像一个工程
1.整体结构
2.编写两个CMakeLists.txt
#外部的CMakeLists.txt
PROJECT(TEST) #工程名
ADD_SUBDIRECTORY(src bin) #将src目录下的文件加入工程,并指定编译生成的文件在bin中
#内部的CMakeLists.txt
ADD_EXECUTABLE(hello test.cc) #对test.cc进行编译(因为已经将其加入工程,所以可以找到),生成的hello会放在bin中
还是进入build进行CMake操作,然后进行make操作,会发现的build目录下生成了一个bin文件夹,进入该文件夹有可执行文件test。
3.安装HelloWorld
首先构建目录树,为了方便演示,先将build中内容删除。
其中新增了COPYRIGHT为版权文件,README为说明文件,runhello.sh为一个运行脚本。
增加安装的命令:
PROJECT(TEST)
ADD_SUBDIRECTORY(src bin)
INSTALL(FILES README COPYRIGHT DESTINATION share/doc/cmake)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
在执行make之后,还需要执行make install来进行安装。
此时可以看到文件安装成功了。
七、建立动态库和静态库
目标:建立一个HelloFunc函数来提供给其他函数使用,该函数可以输出Hello World。建立该函数的头文件以及共享库。
1.结构
2.CMakeLists编写
#外层
PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)
#内层
SET(LIBHELLO_SRC hello.cc)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
其中外层的ADD_SUBDIRECTORY会识别内存的输出文件即动态库,并将其放在执行cmake的文件(build下),执行make之后就可以在bin中看到该动态库libhello.so了。
3.静态库与动态库的构建
在写这里的时候发生了一个让我震惊的事情,所有博客对于这里的描述都是一模一样的,而我没有理清他的逻辑。
所以我经过查阅文档,大概给出了一个可以自洽的逻辑。
(1)动态库与静态库重名构建失败
ADD_LIBRARY(hello SHARED $(LIBHELLO_SRC))
ADD_LIBRARY(hello STATIC $(LIBHELLO_SRC))
虽然后缀不同,但是cmake认为两者是相同的,会构建失败。
(2)解决办法
使用SET_TARGET_PROPERTIES
#建立不同名的动态库和静态库
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
#将静态库改名
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
#这两行我也不知道干啥的,反正要写,好像是清理之前的名字???经过我的测试,不加这两行也可以
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
最后两行代码的具体工作说实话我也没太搞懂,有无大佬解释一下。。。
此时可以看到生成了一个动态库和静态库。
(3)动静态库的安装
使用INSTALL指令来进行安装:
#安装头文件和动静态库
INSTALL(FILES hello.h DESTINATION include/hello)
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
注意这里指定的还是相对路径,他的前缀是usr/local,如果想修改这个前缀,那么可以使用如下指令:
cmake -D CMAKE_INSTALL_PREFIX=/usr
还有一个指令也想介绍一下,那就是生成debug版本的方式
cmake -D CMAKE_BUILD_TYPE=debug
此时就将其修改为了usr路径了。
使用make install来完成安装。
八、使用动态库与静态库
1.链接头文件
当使用include<>包含头文件的时候会发生报错,CMake有专门的函数来帮助找到这个头文件。
INCLUDE_DIRECTORIES(/usr/local/include/hello) #头文件搜索路径
2.链接动态库
TARGET_LINK_LIBRARY:如果你的库在标准的库搜索路径,直接在这里写上库名就好。如果不是可以使用绝对路径。
TARGET_LINK_LIBRARIES(hello /usr/local/lib/libhello.so)
注意,链接动态库需要写在ADD_EXECULABLE之下。他的第一个参数表示的就是要将库中内容加载进的可执行文件。
此时执行make可以生成可执行文件hello。