CMakeList学习

366 阅读4分钟

CMakeList.txt基本语法

cmake_minimum_required关键字

cmake_minimum_required(VERSION 3.6.3)
#指定cmake需要的最低版本

PROJECT关键字

PROJECT(<name> <support_language>) 
#name是工程名,support_language的默认值是所有语言,CXX表示c++,中间用空格隔开

#变量:在cmake中变量定义并不同一般的高级语言,就好比上面的命令,就创建了两个隐式变量
<project_name>_BINARY_DIR
<project_name>_SOURCE_DIR
#这两个变量都是指向工作目录,不过在外部编译时会不同不过为了方便project_name变化导致变量名也要修改,这里预定义了两个另外的变量名:
PROJECT_BINARY_DIR
PROJECT_SOURCE_DIR
#这两个变量都可以在MESSAGE关键字中使用

SET关键字

SET(SCR_LIST <file_name.cpp>)
#SET用于变量赋值,SCR_LIST就是变量名,它将会指向file_name.cpp文件,file_name.cpp可以不止一个,多个文件可以用空格隔开
set(CMAKE_CXX_FLAGS -g -o2 -Wall)
#这条命令等价于在g++编译命令行最后添加-g -o2 -Wall

MESSAGE关键字

MESSAGE()
#用于在终端输出提示信息,第一个参数有三种:
SEND_ERROR:产生报错,生成过程被跳过
SATUS:输出的前缀为--
FATAL_ERROR:立即终止所用cmake过程
例如:
MESSAGE(SATUS "this project binary dir is" ${PROJECT_BINARY_DIR})
输出:
--this project binary dir is /path/to/project

ADD_EXECUTABLE关键字

#用于生成可执行文件!
ADD_EXECUTABLE(execut_file_name ${SCR_LIST})

include_directories关键字

#用于指定头文件搜索路径:<==> g++中的-l
include_directories(/usr/include/dir ./include)
#也可以通过关键字CMAKE_SOURCE_DIR写另一种绝对路径
include_directories(${CMAKE_SOURCE_DIR}/include)
#或者直接写相对路径:
include_directories(include)

link_directories关键字

#用于指定库文件依赖的目录 <==> g++中的-L
link_directories(/usr/lib/dir ./lib)
#相比之下,更加推荐使用下面这条命令
target_link_libraries(${CMAKE_PROJECT_NAME} ${OpenCV_LIBRARIES})

add_compile_options关键字

#用于添加编译参数
add_compile_options(-Wall -std=c++11 -o2)
#Wall输出所有警告信息

外部构建与内部构建

内部构建:直接在项目文件夹中构建,会产生很多过度文件 外部构建:在项目中建立一个build文件夹,在build文件夹中构建,推荐使用

#外部构建和内部构建的不同点就在新建的build文件夹下,对于外部构建:
PROJECT_BINARY_DIR=/path/to/project/bulid
PROJECT_SOURCE_DIR=/path/to/project

在脚本中执行过程如下:
$ mkdir build && cd build
$ cmake ..
$ make #这是在build下构建,最后的可执行文件也是会出现在build中

项目文件构建思路

file_tree:
|__build
|__doc
	|__hello.txt
|__CMakeList.txt(外层)
|__README
|__runhello.sh
|__COPYRIGHT(版权)
|__src(放置工程源代码)
	|__CMakeList.txt(内层)
	|__main.cpp

外层CMakeList.txt与内层CMakeList.txt需要相互连接,在外层的CMakeList.txt就会这样写:

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
#参数src的作用是用于连接src文件夹,参数bin是会新生成一个目录,里面会存储下那些二进制文件,可执行文件和依赖库

内层的CMakeList.txt文件目的是生成可执行文件:

ADD_EXECUTABLE(hello main.cpp)

项目安装

在build之中执行完make接着执行安装install 也有命令是在CMakeList.txt中实现: 用关键字INSTALL 比如安装COPYRIGHT和README,在外层的CMakeList.txt就会这样写:

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
#参数src的作用是用于连接src文件夹,参数bin是会新生成一个目录,里面会存储下那些二进制文件,可执行文件和依赖库
##INSTALL()这个函数会产生一个变量CMAKE_INSTALL_PREFIX=/usr/local/
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
#在这里share/doc/cmake/是相对路径,这里就会默认引用变量${CMAKE_INSTALL_PREFIX}
# 第一个参数FILE表示安装文件,也有PROGRAMS非目标文件比如脚本文件.sh,DIRECTORY是安装目录
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
#这将会安装到/usr/local/bin目录下
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
#在这个标题doc/与doc的不同
#doc/会将doc下的内容直接安装到目标路径share/doc/cmake,而doc会在目标路径下再建立一个doc目录,把内容放到doc下

静态库与动态库构建

静态库文件一般是.a或.lib,动态库的文件一般为.so或.dll 两者的不同在: 静态编译慢,目标文件大,但是目标文件可以脱离依赖直接运行 动态编译快,目标文件小,但是目标文件需要依赖动态库文件执行

文件目录:
|__build
|__CMakeList.txt(外层)
|__lib
	|__CMakeList.txt(内层)
	|__hello.cpp
	|__hello.h

hello.h文件内容如下:

#ifndef HELLO_H
#define Hello_H
void hello_function();
#endif

hello.cpp文件的内容如下:

#include "hello.h"
#include<iostream>
viod hello_function(){
    std::cout<<"hello world"<<std::endl;
}

外层的CMakeList.txt内容就会这样写:

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
#这条命令可以生成命令目标依赖库文件,第二个参数控制生成的是动态库还是静态库
#SHARED最终生成动态库,STATIC最终生成静态库
#第一个参数是最后依赖库的名字,生成过程文件名会自动变成libhello.so,最后文件会出现在build/bin/下

同时生成静态库文件和动态库文件:

#!理论上有一个错误的写法:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
#!这样并不能生成libhello.so和libhello.a,因为他们两个重名了,这样第二条命令生成的文件会覆盖掉第一条所想要生成的动态库文件,最后只会有一个静态库文件,并没有动态库文件,可以考虑给出不同的命名,然后重命名

SET(LIBHELLO_SRC hello.cpp)
#先将静态库文件命名为hello_static,
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
#同时进行重命名,并且将输出目标文件设置为唯一:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
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)

此外,动态库还会有一个版本控制:

libhello.so.1.2
libhello.so => libhello.so.1
libhello.so.1 => libhello.so.1.2
#版本控制的命令:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
#VERSION是指动态库版本,SOVERSION指代API版本

安装共享库与头文件:

INSTALL(FILES hello.h DESTNATION include/hello)

#二进制、静态库、动态库安装的第一个参数是 TARGETS
#ARCHIVE特指静态库 LIBRARY特指动态库 RUNTIME特质可执行的目标二进制文件
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

最后命令行安装的时候要指明安装路径在系统下,以便调用:

$ make -D CMAKE_INSTALL_PREFIX=/usr ..
$ make install

~外部共享库和头文件的使用~


tree:
|__include
	|__swap.h
|__main.cpp
|__CMakeList.txt
|__src
	|__swap.cpp