CMake
1-外部构建,在build 执行 make
文件结构如下
[ 4096] .
├── [ 4096] build 目录
├── [ 92] CMakeLists.txt
└── [ 110] HelloCMake.cpp
内容
CMakeLists.txt 文件名一定不能写成其他
PROJECT(HELLO_CMAKE) //项目名
set (SRC_LIST HelloCMake.cpp) //设置变量
add_executable(hello_cmake ${SRC_LIST)}) //添加源文件 ,生成可执行文件 hello_cmake
定义变量的方式 set (变量名 被指定的)
使用变量的方法是 $(变量名)
add_executable() 参数一 是生成的可执行文件名(目标文件) 参数二是依赖的源文件
#include<iostream>
using namespace std;
int main()
{
cout<<"hello cmake "<<endl;
return Exit_Success;
}
在build目录中执行
cmake ..
截取执行部分效果
-- Configuring done //配置完成
-- Generating done //生成完成
-- Build files have been written to: /home/xxx/CMakeProc/HelloCMake/build //构建文件写入build 文件夹
cmake … 表示从上级目录寻找CMakeList.txt文件,生成 makefile
生成效果
xxx@xxx-virtual-machine:~/CMakeProc/HelloCMake/build$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
此时生成了 makefile ,就可以执行 make 命令
xxx@xxx-virtual-machine:~/CMakeProc/HelloCMake/build$ make
[ 50%] Building CXX object CMakeFiles/hello_cmake.dir/HelloCMake.cpp.o
[100%] Linking CXX executable hello_cmake
[100%] Built target hello_cmake
会多出一个可执行文件,文件名由 executable() 指定
xxx@xxx-virtual-machine:~/CMakeProc/HelloCMake/build$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake hello_cmake Makefile
2输出自定义信息
在CMakeLists.txt 中添加
message(
[SEND_ERROR | STATUS | FATAL_ERROR]
"要输出的信息"
)
其中 SEND_ERROR STATUS FATAL_ERROR 是参数
调整CMakeList.txt 中的内容(注释掉部分)
如果不加参数
PROJECT(HELLO_CMAKE)
set (SRC_LIST HelloCMake.cpp)
message("this is testMessage null para")
# message(STATUS "this is status")
# message(SEND_ERROR "this is send_error")
# message(FATAL_ERROR "this is fatal_error")
message("this is lastest info")
add_executable(hello_cmake ${SRC_LIST})
生成Makefile的过程
this is testMessage null para
this is lastest info
-- Configuring done
-- Generating done
-- Build files have been written to: /home/xxx/CMakeProc/HelloCMake/build
加了 status
PROJECT(HELLO_CMAKE)
set (SRC_LIST HelloCMake.cpp)
#message("this is testMessage null para")
message(STATUS "this is status")
#message(SEND_ERROR "this is send_error")
#message(FATAL_ERROR "this is fatal_error")
message("this is lastest info")
add_executable(hello_cmake ${SRC_LIST})
生成Makefile的过程
-- this is status
this is lastest info
-- Configuring done
-- Generating done
-- Build files have been written to: /home/xxx/CMakeProc/HelloCMake/build
没有给message传参数的直接就输出了拾定的信息。而传递了STATUS参数的会在输出的信息前面加–
加了 SEND_ERROR
PROJECT(HELLO_CMAKE)
set (SRC_LIST HelloCMake.cpp)
#message("this is testMessage null para")
#message(STATUS "this is status")
message(SEND_ERROR "this is send_error")
#message(FATAL_ERROR "this is fatal_error")
message("this is lastest info")//后面的语句不会执行
add_executable(hello_cmake ${SRC_LIST})
生成Makefile的过程
CMake Error at CMakeLists.txt:5 (message):
this is send_error
this is lastest info
-- Configuring incomplete, errors occurred!
See also "/home/xxx/CMakeProc/HelloCMake/build/CMakeFiles/CMakeOutput.log".
加了 FATAL_ERROR
PROJECT(HELLO_CMAKE)
set (SRC_LIST HelloCMake.cpp)
#message("this is testMessage null para")
#message(STATUS "this is status")
#message(SEND_ERROR "this is send_error")
message(FATAL_ERROR "this is fatal_error")
message("this is lastest info") //后面的语句不会执行
add_executable(hello_cmake ${SRC_LIST})
生成Makefile的过程
CMake Error at CMakeLists.txt:6 (message):
this is fatal_error
-- Configuring incomplete, errors occurred!
See also "/home/xxx/CMakeProc/HelloCMake/build/CMakeFiles/CMakeOutput.log".
可以看到,SEND ERROR和FATAL ERROR的信息都输出了,而且生成过程都跳过了因此,
SEND_ERROR只是发送错误跳过生成成过程, 它后面的语句还会执行;而FATAL_ ERROR,从字面可以知道,致命错误,会立即中止过程,它后 面的语句也不再执行
3两个变量
PROJECT_BINARY_DIR
PROJECT_SOURCE_DIR
CMake会为每个项目的二进制目录和源目录
隐式生成两个变量
<project name>_ BINARY DIR
<project name>_ SOURCE DIR
同时也存在这样的两个变量:
PROJECT BINARY DIR = <project name> BINARY DIR
PROJECT_ SOURCE DIR = <project name>_ SOURCE_ DIR
可以修改 CMakeLists.txt ,让其输出 信息
PROJECT(HELLO_CMAKE)
set (SRC_LIST HelloCMake.cpp)
#message("this is testMessage null para")
#message(STATUS "this is status")
#message(SEND_ERROR "this is send_error")
#message(FATAL_ERROR "this is fatal_error")
#message("this is lastest info")
message(${HELLO_CMAKE_BINARY_DIR})
message(${PROJECT_BINARY_DIR})
message("#####################")
message(${PROJECT_SOURCE_DIR})
message(${HELLO_CMAKE_SOURCE_DIR})
add_executable(hello_cmake ${SRC_LIST})
声成makefile的信息
/home/xxx/CMakeProc/HelloCMake/build
/home/xxx/CMakeProc/HelloCMake/build
#####################
/home/xxx/CMakeProc/HelloCMake
/home/xxx/CMakeProc/HelloCMake
PROJECT_BINARY_DIR 对应的是执行 cmake命令所在的目录,这里为build 文件夹
PROJECT_SOURCE_DIR 对应的是源文件所在的目录 就是HelloCmake
4动态库和静态库的构建和使用
文件结构
.
├── build
├── CMakeLists.txt
├── HelloLibrary.cpp
├── HelloLibrary.h
└── Main.cpp
文件内容
main.cpp
#include"HelloLibrary.h"
int main()
{
hello_library();
return 0;
}
HelloLibrary.h
#ifndef __HELLO_LiBRARY__
#define __HELLO_LiBRARY__
void hello_library();
#endif // __HELLO_LIBRARY
HelloLibrary.cpp
#include"HelloLibrary.h"
#include<iostream>
void hello_library()
{
std::cout<<"hello Shared Library!"<< std::endl;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.8 FATAL_ERROR) #设置CMake所需的最低版本。如果使用的CMake版本低于该版本,则会发出致命错误,第二个参数可选, (VERSION [...][FATAL_ERROR]) 设置该工程的cmake最低支持版本,注意 VERSION 不能写成小写,否则会报cmake_minimum_required called with unknown argument version
project (HelloLibrary)
add_library(hello_library SHARED HelloLibrary.cpp) #生成动态库,第二个参数是源文件。 静态库就是 STATIC
include_directories(${PROJECT_SOURCE_DIR}) #给出编译器寻找头文件的目录(这个变量是源文件所在目录)
add_executable(hello_main Main.cpp)
target_link_libraries(hello_main hello_library) #链接动态库
类比 makefile 我们是使用 .h 和 对应的 .cpp(除了主函数的cpp文件) 生成 .o (目标文件) , 目标文件生成库文件 。
链接动态库的方法
生成库(add_library)
设置头文件的搜索路径
(include_directories)
为什么要使用 include_directories()
include_directories
是用来提供找头文件路径的,打个比方,我现在想要#include"cv.h",但是这个cv.h的路径是/usr/local/include/opencv,那么我总不能在主函数头前写#include “/usr/local/include/opencv/cv.h”吧,
这个时候就用到include_directories了,它提供了一个搜索头文件暂时的根目录,即你可以在cmakelists中写上
include_directories(/usr/local/include)来让库文件搜索以/usr/local/include为基础,即在main函数前写上#include “opencv/cv.h"即可
如果存在多个头文件搜索路径 需要用空格隔开
设置库的搜索路径
LINK_DIRECTORIES
如果生成的动态库在第三方路径,需要使用 LINK_DIRECTORIES()
LINK_DIRECTORIES 命令来指定第三方库所在路径,比如,你的动态库在/home/myproject/libs这个路径下,则通过命令:LINK_DIRECTORIES(/home/myproject/libs),把该路径添加到第三方库搜索路径中,这样就可以使用相对路径了,使用TARGET_LINK_LIBRARIES的时候,只需要给出动态链接库的名字就行了。
也可以set(LINK_DIR /Users/haoran/Downloads/wfdb/lib),LINK_DIRECTORIES({LINK_DIR})
官方不建议使用该命令,取而代之的为find_package() find_library()
5同时生成同名的动态静态库
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
这个命令是设置目标的属性,该命令的语法是列出想要更改的所有目标,然后提供接下来想要设置的值。您可以使用该命令任何所需的键值对,然后使用get_property()或get_target_property()命令提取它。
1、建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc
向终端输出Hello World字符串。
2、安装头文件与共享库。
3、编写一个程序,来使用创建的共享库(静态库和动态库)。
首先,新建一个目录,创建MakeList.txt
/home/code/test/build
文件内容
hello.cpp
#include "hello.h"
using namespace std;
void HelloFunc(){
cout << "Hello World/n";
}
hello.h
#ifndef HELLO_H
#define HELLO_H
#include <stdio.h>
void HelloFunc();
#endif
同级目录下的CMakeLists.txt
SET (LIBHELLO_SRC hello.cpp)
# SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# 添加动态库,关键词为shared,不需要写全称libhello.so,
# 只需要填写hello即可,cmake系统会自动为你生成 libhello.X
ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC})
# 添加静态库,关键词为static,ADD_LIBRARY (hello STATIC ${LIBHELLO_SRC})
# 仍然用hello作为target名时,是不能成功创建所需的静态库的,
# 因为hello作为一个target是不能重名的, 故把上面的hello修改为hello_static
# 同理,你不需要写全libhello_static.a
# 只需要填写hello即可,cmake系统会自动为你生成 libhello_static.X
ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC})
# 按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同;
# 即:静态库名为 libhello.a; 动态库名为libhello.so ;
# 所以,希望 "hello_static" 在输出时,不是"hello_static",而是以"hello"的名字显示,故设置如下:
SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE (STATUS "This is the hello_static OUTPUT_NAME: " ${OUTPUT_VALUE})
# cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库,
# 因此,在构建libhello.a时,就会清理掉libhello.so.
# 为了回避这个问题,比如再次使用SET_TARGET_PROPERTIES定义 CLEAN_DIRECT_OUTPUT属性。
SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# 按照规则,动态库是应该包含一个版本号的,
# VERSION指代动态库版本,SOVERSION指代API版本。
SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
# 我们需要将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用,
# 在本例中我们将hello的共享库安装到<prefix>/lib目录;
# 将hello.h安装<prefix>/include/hello目录。
INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL (FILES hello.h DESTINATION include/hello)
在build目录中
cmake ..
make
这时,你就可以在lib目录得到一个libhello.so,这就是我们期望的共享库。
如果你要指定libhello.so生成的位置,可以通过:
在主工程文件CMakeLists.txt中修改ADD_SUBDIRECTORY (lib) 指令来指定一个编译输出位置;
或者在 lib/CMakeLists.txt中添加SET (LIBRARY_OUTPUT_PATH <路径>) 来指定一个新的位置。
6添加工程子目录
将源文件放入SRC 目录中,构建生成可执行文件在build文件夹中。
文件结构
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
文件内容
一级的CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(SubDirectory)
add_subdirectory(src bin) #将 src 加入 工程 ,编译输出的结果放入 bin 目录
二级的 CMakeLists.txt
add_executable(sub_directory main.cpp) #生成的可执行文件
二级的 main.cpp
#include<iostream>
using namespace std;
int main()
{
std::cout<<"This is sub directory"<<std::endl;
return 0;
}
在build 中执行 cmake …
bin CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
可以在 bin 中 也可以在当前目录执行 make 执行makefile.
执行后 ,bin 中出现 可执行文件 sub_directory
CMakeFiles cmake_install.cmake Makefile sub_directory
补充:
ADD_ SUBDIRECTORY指令
ADD_ SUBDIRECTORY(src_ dir [bin_ dir]
[EXCLUDE_ FROM_ ALL])
src_ dir: 把src_ dir这个子目录加入工程
bin_ dir: 指定编译输出(包含编译的中间结果)的目录
为bin_ dir ,如果不指定,则默认输出目录为srC_ dir
即与src_ dir同名的目眼录;指定其实就是把src_ dir
重命名为bin_ dir
EXCLUDE_ FROM_ ALL: 将给定的目录从编译中排除,即不编译它
比如一些示例的目录,需要等 T程构建完
后再单独构建的
CMake指定目标保存目录
set(EXECUTABLE_OUTPUT_PATH )
set(LIBRARY_OUTPUT_PATH)
文件结构
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── main.cpp
├── SayHello.cpp
└── SayHello.h
一级CMakeLIsts.txt中的内容
cmake_minimum_required(VERSION 3.1)
project(OutputPath)
add_subdirectory(src)
二级CMakeLIsts.txt中的内容
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)#在build 文件夹下 创建bin文件夹,并在bin中生成执行文件
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) #在build 文件夹下 创建lib文件夹,并在lib中生成库
add_library(SayHello SayHello.cpp)
include_directories(${PROJECT_SOURCE_DIR}/src)# 添加 头文件搜索路径
add_executable(OutputPath main.cpp)
target_link_libraries(OutputPath SayHello)
PROJECT_BINARY_DIR 这个变量是执行cmake 的地方 也就是build
文件内容
SayHello.h
#ifndef __SAYHELLO_H__
#define __SAYHELLO_H__
void Say_Hello();
#endif
SayHello.cpp
#include<iostream>
#include"SayHello.h"
using namespace std;
void Say_Hello()
{
std::cout<<"hello CMake"<<std::endl;
}
进入build 文件夹中
执行 cmake 生成 makefile
bin CMakeCache.txt CMakeFiles cmake_install.cmake lib Makefile src
但此时 bin 和 lib 中为空目录,src 中存放了中间文件
执行 make(可以在src 目录内,也可以在build 的同级目录) ,生成可执行文件和库文件
查看 bin 和 lib 中的文件
xxx@xxx-virtual-machine:~/CMakeProc/OutputPath/build$ tree bin lib
bin
└── OutputPath # 是可执行文件
lib
└── libSayHello.a # 库文件