当多个人要使用不同的语言或者编译器来开发一个项目的时候,最终要输出一个可执行文件或者共享库(dll,so等等),CMake使得所有的操作都是通过编译CMakeLists.txt来实现的。
学习CMake的目的是为了将来处理大型的C/C++项目,可以先看下面的例子参照上面的关键字来进行学习。
PROJECT:用来指定工程的名字以及支持的语言,支持的语言不填的话,默认是支持所有语言。
PROJECT(HELLO) #默认支持所有语言
PROJECT(HELLO C CXX) #指定支持C和C++
执行该条指令的时候隐式定义了两个CMAKE变量,分别是
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),则必须加双引号,如果没有加不加都可以。
MESSAGE:向终端输出用户自定义的信息。
主要包含三种信息:
MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR}) #此时的输出带有前缀--
ADD_EXECUTABLE:生成可执行文件。
ADD_EXECUTABLE(test ${SRC_LIST}) #将变量SRC_LIST中文件编译成可执行文件test
ADD_EXECUTABLE(test test.cc) #这样写也可以,不使用变量
其中test.cc的后缀可以不写,会自动去找.c或者.cc,但是不推荐这样写。
ADD_SUBDIRCTORY:
ADD_SUBDIRECTORY(src bin) #将src的子目录加入工程,并指定编译结果输出路径为bin目录。
该指令会自动创建一个bin目录,如果不指定则结果会放在build/src即SOURCE_DIR目录。
CMakeLists是逐层进行执行的:
ADD_SUBDIRECTORY可以识别出指定路径中CMakeLists中的ADD_EXECUTABLE生成的可执行文件是一个输出文件,并会将该文件添加到指定的输出文件路径。这个输出文件也可以是一个动态库或者静态库。
INSTALL:为安装关键字,他可以用来安装一个文件,也可以用来安装脚本等。
安装文件:
INSTALL(FIELS README DESTINATION share/doc/cmake/)
安装脚本:
INSTALL(PROGRAMES runhello.sh DESTINATION bin)
安装目录:
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
注意仅仅cmake是不能够进行安装的,而是在生成makefile之后还需要进行make install操作。才算安装成功。
ADD_LIBRARY:用来将源文件创建成动态库,即将test.cc编程libtest.so
ADD_LIBRARY(hello SHARED test.cc)
SET_TARGET_PROPERTIES:用来设置输出的名称,对于动态库,还可以指定动态库版本和API版本。
这个在动静态库中结合场景具体介绍。
这里主要编写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没有发生变化。
强烈推荐使用外部构建。

#外部的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。

首先构建目录树,为了方便演示,先将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。建立该函数的头文件以及共享库。

#外层
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了。

在写这里的时候发生了一个让我震惊的事情,所有博客对于这里的描述都是一模一样的,而我没有理清他的逻辑。
所以我经过查阅文档,大概给出了一个可以自洽的逻辑。
ADD_LIBRARY(hello SHARED $(LIBHELLO_SRC))
ADD_LIBRARY(hello STATIC $(LIBHELLO_SRC))
虽然后缀不同,但是cmake认为两者是相同的,会构建失败。
使用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)
最后两行代码的具体工作说实话我也没太搞懂,有无大佬解释一下。。。

此时可以看到生成了一个动态库和静态库。
使用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来完成安装。
当使用include<>包含头文件的时候会发生报错,CMake有专门的函数来帮助找到这个头文件。
INCLUDE_DIRECTORIES(/usr/local/include/hello) #头文件搜索路径
TARGET_LINK_LIBRARY:如果你的库在标准的库搜索路径,直接在这里写上库名就好。如果不是可以使用绝对路径。
TARGET_LINK_LIBRARIES(hello /usr/local/lib/libhello.so)
注意,链接动态库需要写在ADD_EXECULABLE之下。他的第一个参数表示的就是要将库中内容加载进的可执行文件。

此时执行make可以生成可执行文件hello。