![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2676b35d3ac8533f1437f0dfe948e67f.png)
简单cmake
在开篇的思维导图中已经展示了该实例的文件布局。其中main.cpp/CmakeLists.txt
内容如下所示:
main.cpp
#include <iostream>
using namespace std;
int main(){
std::cout << "hello world" << endl;
return 0;
}
CmakeLists.txt
PROJECT(HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "THIS is a binary dir" ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is source dir" ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
接着我们来看下构建过程,
- 在
./build
目录下执行cmake ..
,..
表示上一级目录;此时build
目录下产生如下文件:
[root@instance-avkrpt0w t1]# ls build/
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
在这些文件中,最重要的当然是Makefile
文件啦。
- 同样的
build
目录下执行make
命令,此刻产生可执行文件hello
对于上述使用过的指令做一个简单的解释:
PROJECT(projectname [CXX] [C] [Java])
,用于指定工程名称,同时指定支持语言。该语句隐形定义两个cmake变量,PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR
。SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
, docstring : 文档字符串,显示定义变量。MESSAGE([SEND_ERROR] | STATUS | fATAL_ERROR], "message to display....")
,向终端输出用户定义的信息。-
SEND_ERROR
, 产生错误,生成过程跳过。 STATUS
,输出信息- ‘FATAL_ERROR’,终止所有cmake过程。
-
ADD_EXECUTALBE(hello ${SRC_LIST})
,生成一个hello的可执行文件。SRC_LIST
,源文件列表,一般用过SET
定义。
更好一点的cmake
在开篇的思维导图中已经展示了该实例的文件布局。其中文件具体如下:
./src/main.cpp
:和上述一样,打印hello , world。COPYRIGHT, README
,随意写一些东西runhello.sh
脚本,用来调用hello二进制。./bin
,保存二进制文件与中间文件。- 主工程
./CMakeLists.txt
如下:
PROJECT(HELLO)
# 将src源文件产生的中间文件和目标二进制存放在project_binary_dir/bin目录下
ADD_SUBDIRECTORY(src bin)
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
./src/CMakeLists.txt
如下:
# 指定最终的目标二进制的位置。
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
ADD_EXECUTABLE(hello main.cpp)
同样,构建过程如下:
- 目录工程下
mkdir build && cd build
- 执行
cmake -D CMAKE_INSTALL_PREFIX=/tmp/t2/usr
,可以通过cmake --help看 -D的使用说明。 - make
- make install ,显示如下
[100%] Built target hello
Install the project...
-- Install configuration: ""
-- Up-to-date: /tmp/t2/usr/share/doc/cmake/t2/COPYRIGHT
-- Up-to-date: /tmp/t2/usr/share/doc/cmake/t2/README
-- Up-to-date: /tmp/t2/usr/bin/runhello.sh
-- Installing: /tmp/t2/usr/share/doc/cmake/t2
-- Up-to-date: /tmp/t2/usr/share/doc/cmake/t2/hello.txt
该文件中使用的cmake指令如下:
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
,向当前工程添加存放源文件的子目录,并可指定中间二进制和目标二进制存放位置。EXCLUDE_FROM_ALL
是将这个目录从编译过程中排除。- 安装需要
INSTALL
指令和CMAKE_INSTALL_PREFIX
变量相互配合,详细介绍请看cmake practice。install
指令针对于FILES\PROGRAMS\DIRECTORY
有不同的可选项。CMAKE_INSTALL_PREFIX
就是安装路径前缀,默认为/usr/local
- 注意:
CMakeLists.txt
中两种定制二进制文件和中间文件输出方法的区别。
静态库与动态库
在开篇的思维导图中已经展示了该实例的文件布局。其中文件具体如下:
./CMakeLists.txt
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
./lib/CMakeLists.txt
SET(LIBHELLO_SRC hello.cpp)
# 设置lib输出
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# 动态库
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
# 静态库库
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
# 静态库改名成与动态库同名库
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
# 与SET_TARGET_PROPERTIES相对应
GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
# 设置不同版本好和API号
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
MESSAGE(STATUS "this is the hello_static outputname" ${OUTPUT_VALUE})
# 安装共享库和头文件
INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
./lib/hello.h
#ifndef HELLO_H
#define HELLO_H
void HelloFunc();
#endif
./lib/hello.cpp
#include "hello.h"
#include <iostream>
using namespace std;
void HelloFunc(){
cout << "hello wold \n";
}
接着就是执行cmake -D CMAKE_INSTALL_PREFIX=/usr/local
----> make
----> make install
。执行该命令显示如下:
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/local/lib/libhello.so.1.2
-- Up-to-date: /usr/local/lib/libhello.so.1
-- Up-to-date: /usr/local/lib/libhello.so
-- Up-to-date: /usr/local/lib/libhello.a
-- Installing: /usr/local/include/hello/hello.h
这段CMake代码需要注意以下几点:
-
如何生成动态库与静态库;
-
如何安装动态库与其头文件。
-
如何设置同名静态库与静态库。
这三个问题,在CMakeList.txt中已经详细介绍了。
使用静态库与动态库
文件结构与上述2一致,具体如下:
- 在
./src/main.cpp
#include "hello.h"
int main(){
HelloFunc();
return 0;
}
./CMakeList.txt
PROJECT(NEWHELLO)
ADD_SUBDIRECTORY(src)
./src/CMakeLists.txt
# 1头文件
INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(main main.cpp)
# 2动态库
TARGET_LINK_LIBRARIES(main hello)
# 2静态库
# TARGET_LINK_LIBRARIES(main libhello.a)
在第三章中,我们已经安装了hello的静态库、动态库、头文件,所以只需要包含其,头文件路径和hello库的路径即可,三条指令如上1,2,3
。
当生成可执行文件是,我们可以通过ldd main
查看库信息。
**注意:**可以能在执行动态链接产生的可执行文件,会报错。这是因为链接器找不到库文件,可以通过以下ld.so.conf
配置文件中插入一行/usr/lib
:
$ vim /etc/ld.so.conf
/usr/lib
# 更新
$ ldconfig
关于头文件与库文件搜索路径:
-
如果头文件与库文件安装在常规目录(
/usr/local/include
和/usr/local/lib
),那么我们在使用头文件与库文件的时候,可以当作是标准库一样使用。 -
如果头文件与库文件不是安装在这类路径上,我们就需要自己指定头文件与库文件搜索路径,这里有以下三种方法:
- 在
INCLUDE_DIRECTORIES()
和LINK_DIRECTORIES()
指定头文件和库文件的绝对目录。 - 通过
FIND_PATH
和FIND_LIBRARY
实现1的功能,这里我们可以根据是否使用环境变量CMAKE_INCLUDE_PATH
和CMAKE_LIBRARY_PATH
分出以下两种实现方式:
# 方法一,未使用环境变量 ---------------------CMake_Lists.txt---------------------- find_path(myHeader hello.h /usr/include/hello) IF(myHeader) INCLUDE_DIRECTORIES(${myHeader}) ENDIF(myHeader) ADD_EXECUTABLE(main main.cpp) find_library(LIBHELLO_PATH hello /tmp/t2/lib) IF(LIBHELLOPATH) MESSAGE(FATAL ERROR "libhello not found") ENDIF(NOT LIBHELLO_PATH) TARGET_LINK_LIBRARIES(main ${LIBHELLO_PATH}) -------------------------------------------- # 方法二,使用环境变量 export CMAKE_INCLUDE_PATH=/usr/include/hello export CMAKE_LIBRARY_PATH=/tmp/t2/lib ------------------------CMakeLists.txt-------------- find_path(myHeader hello.h) IF(myHeader) INCLUDE_DIRECTORIES(${myHeader}) ENDIF(myHeader) ADD_EXECUTABLE(main main.cpp) find_library(LIBHELLO_PATH hello) IF(LIBHELLOPATH) MESSAGE(FATAL ERROR "libhello not found") ENDIF(NOT LIBHELLO_PATH) TARGET_LINK_LIBRARIES(main ${LIBHELLO_PATH})
- 在
FIND_PACKAGE命令使用
这里也就是将上述find_path
和find_lbrary
在封装在find_package
中。对于一些常用的库,cmake已经帮我写好了相应的find<name>.cmake
模块,但是对于自定义的库,我们需要自己编写该模块,感兴趣的朋友可看《cmake实践》
参考
- 《CMake实践》