大多数的linux系统是预装cmake的,windows我们不做讨论
sudo apt-get install camke
我们在当前目录下写一个cpp程序
#main.cpp
#include
int main(){
std::cout << "hello word" << std::endl;
}
写一个CMakeLists.txt,也就是Cmake的配置文件
#CMakeLists.txt
PROJECT (HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
在当前目录下执行cmake,生成makefile
cmake .
使用make命令进行编译
make
我们发现在当前的目录下生成了很多中间文件和一个最终生成的二进制可执行文件,hello。
我们在上面的CMakeList.txt中有一些语法我们是不清楚的,现在我们来解释一下。
首先CMakeList中的指令是不区分大小写的,但是指令中的内容是区分大小写的。也就是PROJECT和project是等价的。但是PROJECT(HELLO)和PROJECT(hello)并不等价。
可以用来指定工程的名字和支持的语言,默认支持所有语言
PROJECT (HELLO) # 指定了工程的名字,并且支持所有语言—建议
PROJECT (HELLO CXX) # 指定了工程的名字,并且支持语言是C++
PROJECT (HELLO C CXX) # 指定了工程的名字,并且支持语言是C和C++
_BINARY_DIR # 本例中是 HELLO_BINARY_DIR
_SOURCE_DIR # 本例中是 HELLO_SOURCE_DIR
# 在实际使用的时候,会自动定义两个变量PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,所以其实不用理会
更像是宏定义,或者参数设置
SET(SRC_LIST main.cpp) # SRC_LIST变量就代表了main.cpp
SET(SRC_LIST "main.cpp") # 和上面等价,但是如果文件名中有空格,那么只能使用这种方式
SET(SRC_LIST main) # 会自动寻找main.cpp,但是不要这样写,因为会有main.cpp和main同时存在的情况
向终端输出用户自定义的信息
主要包含三种信息:
SEND_ERROR 产生错误,生成过程被跳过。
SATUS 输出前缀为—的信息。
FATAL_ERROR 立即终止所有 cmake 过程.
生成可执行文件
ADD_EXECUTABLE(hello ${SRC_LIST})
ADD_EXECUTABLE(hello main.cpp)
ADD_EXECUTABLE(hello main1.cpp main1.cpp) # 多个参数用空格分隔
ADD_EXECUTABLE(hello main1.cpp;main1.cpp) # 多个参数用分号分隔
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
# source_dir 源文件目录
# binary_dir 二进制文件目录
# EXCLUDE_FROM_ALL 将那些目录从编译中排除
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
# hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
# SHARED,动态库 STATIC,静态库
# ${LIBHELLO_SRC} :源文
上面的例子就是内部构建,虽然也可以用,但是存在一些问题,就是很乱,不像一个工程,一旦文件变多不好清理,我们可以使用外部构建的方式使得文件目录变得清晰起来。
我们新建一个工程目录,在工程目录下创建以下文件
为工程添加一个子目录 src,用来放置工程源代码
添加一个子目录 doc,用来放置这个工程的文档 hello.txt
在工程目录添加文本文件 COPYRIGHT(版权信息), README(调用须知)
在工程目录添加一个脚本,用来调用 hello 二进制
将构建后的目标文件放入构建目录的 bin 子目录
将 doc 目录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/
我们创建完的工程目录如下
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│ └── hello.txt
├── README
├── runhello.sh
└── src
├── CMakeLists.txt
└── main.cpp
我们修改我们主目录的CMakeLists.txt
PROJECT(HELLO)
# 链接src 链接bin 但是这个bin目录是会在build中生成的,所以我们其实可以写成../bin,这样的话就是在主目录生成了
ADD_SUBDIRECTORY(src bin)
# 安装文件
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake)
# 安装脚本
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
# 安装目录
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
我们修改我们src目录的CMakeLists.txt
ADD_EXECUTABLE(hello main.cpp)
我们进入build目录
camke ..
make程序
make
我们进入工程的主目录bin目录下就可以找到我们的二进制文件了。这时候我们的项目已经有点正经项目的样子了。
安装可以简单理解为将一些生成的文件或者自有的文件放在计算机的指定目录中方便调用。
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
FILES:文件
DESTINATION:相对路径:CMAKE_INSTALL_PREFIX 默认是在 /usr/local/,当然也可以写绝对路径,或者设置CMAKE_INSTALL_PREFIX参数,cmake -DCMAKE_INSTALL_PREFIX=/usr
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
说明:实际安装到的是 /usr/bin
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
DIRECTORY 后面连接的是所在 Source 目录的相对路径
注意:doc 和 doc/有很大的区别
目录名不以/结尾:这个目录将被安装为目标路径下的
目录名以/结尾:将这个目录中的内容安装到目标路径
cmake ..
make
make install
动态库的静态库的区别
├── build
├── CMakeLists.txt
└── lib
├── CMakeLists.txt
├── hello.cpp
└── hello.h
hello.h中的内容
#ifndef HELLO_H
#define Hello_H
void HelloFunc();
#endif
hello.cpp中的内容
#include "hello.h"
#include
void HelloFunc(){
std::cout << "Hello World" << std::endl;
}
项目中的cmake内容
PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)
lib中CMakeLists.txt中的内容
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
// 如果用这种方式,只会构建一个动态库,不会构建出静态库,虽然静态库的后缀是.a
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
// 修改静态库的名字,这样是可以的,但是我们往往希望他们的名字是相同的,只是后缀不同而已
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
//对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
//cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
本例中我们将 hello 的共享库安装到/lib目录,
将 hello.h 安装到/include/hello 目录
//文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)
//二进制,静态库,动态库安装都用TARGETS
//ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
安装的时候,指定一下路径,放到系统下
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
准备工作,新建一个目录来使用外部共享库和头文件
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
main.cpp
#include
int main(){
HelloFunc();
}
我们发现,报错了,找不到头文件,这个很好解决,就是我们的hello.h放在了hello/hello.h这里,所以我们可以
#include
但是很不方便,我们需要区分目录,很麻烦,所以我们可以在CMakeLists.txt中加入头文件搜索路径
INCLUDE_DIRECTORIES(/usr/include/hello)
cmake一下,还是错的,undefined reference to `HelloFunc()',没有找到函数,这个其实也很好解释,你是搞了头文件了,但是没有库啊,你并没有链接库,所以怎么能找到函数呢。我们可以添加一下这个链接库
# 一定要写在add_executable后面
# 添加静态库,直接整合到程序中
TARGET_LINK_LIBRARIES(hello libhello.a)
# 添加动态库,不整合,需要时调用
TARGET_LINK_LIBRARIES(hello libhello.so)