CMake入门(二)

CMake

请先看完上一篇博客:CMake入门(一)

一、说明

  • CMake的命令是不区分大小写的;但是定义的变量、参数区分大小写;
  • 参数是用 空格 或者 分号;隔开的;
  • 使用 ${var}引用变量,其中 var是要引用变量的名字;
  • 引号可加可不加,如果字符串中 有空格 就必须加引号。比如 MinGW Makefiles就必须加引号,"MinGW Makefiles"

二、概念

  • 目标文件(target)分为:可执行文件(add_executable) 和 库文件(add_library);
  • 命令(cmake-command):也就是后面要讲的命令;
  • 变量(cmake-variable):是 CMake 内置的以 CMAKE_开头的变量;
  • 属性(cmake-properties):文件/文件夹都有各自的属性

三、常用命令

cmake_minimum_required

该命令的作用是设置 CMake 的最低版本,低于这个版本的就不行。

格式:

cmake_minimum_required(VERSION <min>)

例子:

# 这里设置 CMake 的最低版本为 3.10
cmake_minimum_required(VERSION 3.10)
project

该命令的作用是设置 项目名

格式:

# <PROJECT-NAME> 是设置项目名 ; <language-name> 是指定项目所使用的语言,比如C++ 就是 CXX
project(<PROJECT-NAME> [<language-name>...])

project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])

# 项目名会被存储在变量 PROJECT_NAME 和 CMAKE_PROJECT_NAME 中
# PROJECT_SOURCE_DIR 等价于 <PROJECT-NAME>_SOURCE_DIR
# PROJECT_BINARY_DIR 等价于 <PROJECT-NAME>_BINARY_DIR

# 如果定义了版本号
# 版本号被保存在 PROJECT_VERSION 和 <PROJECT-NAME>_VERSION 中
# 主版本号被保存在 PROJECT_VERSION_MAJOR 和 <PROJECT-NAME>_VERSION_MAJOR 中
# 次版本号被保存在 PROJECT_VERSION_MINOR 和 <PROJECT-NAME>_VERSION_MINOR 中

例子:

# 设置项目名为 demo
project(demo)
#设置项目名为 demo ; 指定项目的语言为 C C++
project(demo C CXX)
#设置项目名为 demo ; 设置项目的版本号为 2.3 ; 指定项目的语言为 C++
project(demo VERSION 2.3 LANGUAGES CXX)
add_executable

用指定的源文件为项目添加可执行文件。

格式:

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               [source1] [source2 ...])

# <name>即生成可执行文件的名字(与项目名没有关系),在一个项目中必须唯一
# 如windows系统会生成<name>.exe文件

例子:

# 把 main.cpp 添加为可执行文件
add_executable(demo main.cpp)
message

该命令的作用是打印指定信息。

格式:

message([<mode>] "message text" ...)

# STATUS 前缀为--的信息
# SEND_ERROR 产生错误,跳过生成过程
# FATAL_ERROR 产生错误,终止运行

例子:

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(demo VERSION 1.1)

# 打印 src 文件夹路径
message(${demo_SOURCE_DIR})

# 打印 build 文件夹路径
message(${demo_BINARY_DIR})

#打印项目版本号
message(${PROJECT_VERSION})

#打印主版本号
message(${PROJECT_VERSION_MAJOR})

#打印次版本号
message(${PROJECT_VERSION_MINOR})

add_executable(demo main.cpp)

demo/build文件夹下执行命令:

cmake -G "MinGW Makefiles" ../src

结果:

在这里插入图片描述

set

该命令是将一个变量设置为指定值。

格式:

set(<variable> <value>)

例子:

cmake_minimum_required(VERSION 3.10)

project(demo VERSION 1.1)

#设置C++ 11 标准
set(CMAKE_CXX_STANDARD 11)

#设置输出文件位置
# 设置运行时目标文件(exe、dll)的输出位置
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 设置存档目标文件(lib、a)的输出位置
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

#设置变量
set(Genshin "感觉不如原神")
set(msg "Hello,World!!!")
set(str "hahahhhaha")

# 分别打印这些变量
message(${Genshin})
message(${msg})

#用 STATUS 模式打印
message(STATUS ${str})

add_executable(demo main.cpp)

demo/build文件夹下执行命令:

cmake -G "MinGW Makefiles" ../src

结果:

在这里插入图片描述

option

该命令是定义一个开关,默认是 OFF模式。

格式:

option(<variable> "<help_text>" [value])

# value的值为 ON 或 OFF ,默认为 OFF
# 命令行 -D<variable>=ON/OFF

例子:

# 定义一个开关 btn
option(btn "click" ON)
configure_file

该命令是 将输入文件指定内容替换之后生成输出文件。

格式:

configure_file(<input> <output>)

# 输入文件中形如 @VAR@ 或 ${VAR} 的字符串会被替换为这些变量的当前值,如果未定义则被替换为空字符串
# 其他规则见下
configure_file(<input> <output>)

#cmakedefine VAR ...

#会被替换为以下两行之一,取决于VAR是否被设置

#define VAR ...
/* #undef VAR */

例子:

demo/src目录下,新建一个文件 foo.h.in

foo.h.in

#cmakedefine FOO_ENABLE
#cmakedefine FOO_STRING "@FOO_STRING@"

CMakeLists.txt中加上以下这部分代码:

#设置一个名为 FOO_ENABLE 的开关
option(FOO_ENABLE "Enable Foo" ON)
#如果开关是打开的 就设置 FOO_STRING 的值为 "foo"
if(FOO_ENABLE)
	set(FOO_STRING "foo")
endif()
# 输入是foo.h.in文件,输出是 foo.h
configure_file(foo.h.in foo.h)

demo/build文件夹下执行命令:

cmake -G "MinGW Makefiles" ../src

结果:

build目录下生成了 foo.h文件,并且它的内容如图所示:

在这里插入图片描述

FOO_ENABLE的模式设置为 OFF,再执行一次刚才的命令:

#设置一个名为 FOO_ENABLE 的开关
#模式设置为 OFF
option(FOO_ENABLE "Enable Foo" OFF)

#如果开关是打开的 就设置 FOO_STRING 的值为 "foo"
if(FOO_ENABLE)
	set(FOO_STRING "foo")
endif()

# 输入是foo.h.in文件,输出是 foo.h
configure_file(foo.h.in foo.h)

结果为:

在这里插入图片描述

所以得出结论:

// 如果FOO_ENABLE为ON,则生成以下内容的.h文件
#define FOO_ENABLE
#define FOO_STRING "foo"

// 如果FOO_ENABLE为OFF,则生成以下内容的.h文件
/* #undef FOO_ENABLE */
/* #undef FOO_STRING */

include_directories

该命令会指定所有目标的头文件路径。

格式:

include_directories(dir1 [dir2 ...])

# 目录会被添加到当前文件的 INCLUDE_DIRECTORIES 属性中
# 当前文件的每一个目标文件的 INCLUDE_DIRECTORIES 属性也会添加该目录
target_include_directories

该命令会指定目标的头文件路径。

格式:

target_include_directories(<target>
  						   <INTERFACE|PUBLIC|PRIVATE> [items1...]
  						   [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
  						   
# 目标文件有 INCLUDE_DIRECTORIES 和 INTERFACE_INCLUDE_DIRECTORIES 两个属性
# INCLUDE_DIRECTORIES 对内头文件目录
# INTERFACE_INCLUDE_DIRECTORIES 对外头文件目录
INCLUDE_DIRECTORIESINTERFACE_INCLUDE_DIRECTORIES
PRIVATE
INTERFACE
PUBLIC

详细请阅读这篇文章:cmake:target_** 中的 PUBLIC,PRIVATE,INTERFACE

此时我们在 main.cpp中添加如下代码,尝试打印 foo.h中的宏 FOO_STRING

在这里插入图片描述
main.cpp

#include<iostream>

#include "foo.h"

using namespace std;

int main(){
    cout<<"Hello World!!!"<<endl;

   //打印 foo.h 中定义的这个宏
    cout<<"FOO_STRING : "<<FOO_STRING<<endl;
    return 0;
}

当我们执行 cmake --build .进行编译时,发生了如下报错:

在这里插入图片描述
意思是找不到 foo.h这个文件。

因为这个 foo.h文件是存在于 build/目录下的,所以我们需要在 CMakeLists.txt加入以下这句命令,把 build目录加入到其中:

target_include_directories(demo 
                           PUBLIC ${PROJECT_BINARY_DIR})

再重新 编译、运行。

在这里插入图片描述
这次就成功了!!

add_subdirectory

该命令是添加源文件目录的。

格式:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

# binary_dir 指定编译结果存放的位置
add_library

该命令是用用指定的源文件生成库。

格式:

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])

# STATIC 静态库
# SHARED 动态库
# 生成的库文件名为 lib<name>.xxx
target_link_libraries

该命令是为目标链接库。

格式:

target_link_libraries(<target>
                      <PRIVATE|PUBLIC|INTERFACE> <item>...
                     [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

# item 可以是target名、绝对路径(必须保证文件存在)
区别
# 头文件目录
include_directories()
target_include_directories()

# 链接时库目录
link_directories()
target_link_directories()

# 链接库
link_libraries()
target_link_libraries()

# 都推荐使用以target_开头的函数
install

该命令会把指定的文件移动到对应的文件夹下面。

格式:

install(TARGETS <target> DESTINATION <dir>)
install(FILES <file> DESTINATION <dir>)
install(PROGRAMS <非目标文件的可执行程序> DESTINATION <dir>)	# 如脚本
install(DIRECTORY <dir> DESTINATION <dir>)	# 安装目录

例子:

install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
install(DIRECTORY doc/ DESTINATION doc)

四、库的生成和链接

1.使用自己编写的库
编写静态 addition

(1) 在 src文件夹下,创建一个 calc文件夹。再在这个 src/calc/文件夹下面创建四个文件,addition.haddition.cppsubstract.hsubstract.cpp

addition.h

int add(int,int);

addition.cpp

int add(int a,int b){
    return a + b;
}

substract.h

int sub(int,int);

substract.cpp

int sub(int a,int b){
    return a - b;
}

(2) 在 src/CMakeLists.txt添加如下命令:

# 添加子目录 calc
add_subdirectory(calc)

(3) 在 src/calc/文件夹下面,创建它的 CMakeLists.txt

src/calc/CMakeLists.txt

# 生成加法静态库
add_library(addition STATIC addition.cpp)

(4) 在 src/CMakeLists.txt链接生成的静态库addition

src/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(demo VERSION 1.1)

#设置C++ 11 标准
set(CMAKE_CXX_STANDARD 11)

#设置输出文件位置
# 设置运行时目标文件(exe、dll)的输出位置
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 设置存档目标文件(lib、a)的输出位置
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

#设置一个名为 FOO_ENABLE 的开关
option(FOO_ENABLE "Enable Foo" ON)
#如果开关是打开的 就设置 FOO_STRING 的值为 "foo"
if(FOO_ENABLE)
	set(FOO_STRING "foo")
endif()
# 输入是foo.h.in文件,输出是 foo.h
configure_file(foo.h.in foo.h)

add_subdirectory(calc)


add_executable(demo main.cpp)

# 链接生成的加法静态库 addition
target_link_libraries(addition)


# 把 build 目录加到
target_include_directories(demo 
                           PUBLIC "${PROJECT_BINARY_DIR}")

(5) 编译运行。

main.cpp调用这个加法库中的 add函数:

#include<iostream>

#include "foo.h"
#include "addition.h"

using namespace std;

int main(){

    cout<<"Hello World!!!"<<endl;

    //打印 foo.h 中定义的这个宏
    cout<<"FOO_STRING : "<<FOO_STRING<<endl;
    
    //调用 addition 库中的 add 函数
    cout<<"10 + 20 = "<<add(10,20)<<endl;
    
    return 0;
}

接着再构建项目,编译。
在这里插入图片描述
又显示 addition.h找不到了。

所以我们还要把 src/calc目录加到扫描头文件的那个命令中:

# 把 build 目录 和 src/calc 加到
target_include_directories(demo 
                           PUBLIC "${PROJECT_BINARY_DIR}"
                           PUBLIC "${PROJECT_SOURCE_DIR}/calc")

接着再编译就能成功了。

在这里插入图片描述

编写动态 substract

(1) 首先在 src/calc/CMakeLists.txt中添加生成 动态substract库的命令。

# 生成加法静态库
add_library(addition STATIC addition.cpp)

# 生成减法动态库
add_library(substract SHARED substract.cpp)

(2) 在 src/CMakeLists.txt中添加 链接 动态substract库的命令。

cmake_minimum_required(VERSION 3.10)

project(demo VERSION 1.1)

#设置C++ 11 标准
set(CMAKE_CXX_STANDARD 11)

#设置输出文件位置
# 设置运行时目标文件(exe、dll)的输出位置
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 设置存档目标文件(lib、a)的输出位置
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

#设置一个名为 FOO_ENABLE 的开关
option(FOO_ENABLE "Enable Foo" ON)
#如果开关是打开的 就设置 FOO_STRING 的值为 "foo"
if(FOO_ENABLE)
	set(FOO_STRING "foo")
endif()
# 输入是foo.h.in文件,输出是 foo.h
configure_file(foo.h.in foo.h)

add_subdirectory(calc)


add_executable(demo main.cpp)

#链接加法静态库
target_link_libraries(demo PUBLIC addition)

#链接减法动态库
target_link_libraries(demo PUBLIC substract)


# 把 build 目录加到
target_include_directories(demo 
                           PUBLIC "${PROJECT_BINARY_DIR}"
                           PUBLIC "${PROJECT_SOURCE_DIR}/calc")

(3) 在 main.cpp中调用 sub方法。

#include<iostream>

#include "foo.h"
#include "addition.h"
#include "substract.h"

using namespace std;

int main(){

    cout<<"Hello World!!!"<<endl;

    //打印 foo.h 中定义的这个宏
    cout<<"FOO_STRING : "<<FOO_STRING<<endl;

    cout<<"10 + 20 = "<<add(10,20)<<endl;

    cout<<"10 - 20 = "<<sub(10,20)<<endl;
    
    return 0;
}

(4) 开始编译、运行。

在这里插入图片描述
运行成功!!!

2.使用外部的库
编写乘法静态库

(1) 我们打开 CLion ,生成一个乘法的静态库 multipy.a

在这里插入图片描述

选择创建一个 C++ 14 的静态库。

在这里插入图片描述
library.cpp , library.cpp改为 multiply.h , multiply.cpp

在这里插入图片描述
(2) 把 multiply.h , multiply.cpp的内容改为如下:

multiply.h

#ifndef LIBRARY_MULTIPLY_H
#define LIBRARY_MULTIPLY_H

int mul(int,int);

#endif //LIBRARY_MULTIPLY_H

multiply.cpp

#include "multiply.h"

int mul(int a,int b){
    return a * b;
}

(3) 把 CMakeLists.txt的内容改为如下:

cmake_minimum_required(VERSION 3.10)

project(library)

set(CMAKE_CXX_STANDARD 14)

# 生成 multiply 静态库
add_library(multiply STATIC multiply.cpp)

(4) build

在这里插入图片描述
在这里插入图片描述

(5) 在 demo文件下面创建一个 other_lib文件夹,把生成的静态库 multiply.a和头文件 multiply.h都放入到这个 other_lib文件夹下。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(6) 把 other_lib添加到扫描的路径中,在 demo/src/CMakeLists.txt添加如下命令。

# 把 build 目录加到

#因为 other_lib 是和 src 同级的
target_include_directories(demo 
                           PUBLIC "${PROJECT_BINARY_DIR}"
                           PUBLIC "${PROJECT_SOURCE_DIR}/calc"
                           PUBLIC "${PROJECT_SOURCE_DIR}/../other_lib")

(7) 链接静态库 multiply.a,在 demo/src/CMakeLists.txt添加如下命令。

#链接外部 乘法静态库
#因为外部库我们可能不知道名字,所以这里直接使用绝对路径

target_link_libraries(demo PUBLIC "G:/demo/other_lib/libmultiply.a")

(8) 在 main.cpp中调用 mul函数。

#include<iostream>

#include "foo.h"
#include "addition.h"
#include "subtract.h"
#include "multiply.h"

using namespace std;

int main(){

    cout<<"Hello World!!!"<<endl;

    //打印 foo.h 中定义的这个宏
    cout<<"FOO_STRING : "<<FOO_STRING<<endl;

    cout<<"10 + 20 = "<<add(10,20)<<endl;

    cout<<"10 - 20 = "<<sub(10,20)<<endl;

    cout<<"10 * 20 = "<<mul(10,20)<<endl;
    
    return 0;
}

(9) 编译、运行

在这里插入图片描述
运行成功!!!

编写除法动态库

(1) 在刚才 CLion 打开的项目中,创建 division.cpp , division.h两个文件。

在这里插入图片描述
(2) 在 division.cpp , division.h两个文件中定义和实现 divide(int,int)方法。

division.h


#ifndef LIBRARY_DIVISION_H
#define LIBRARY_DIVISION_H

int divide(int,int);

#endif //LIBRARY_DIVISION_H

division.cpp

int divide(int a,int b){
    return a / b;
}

(3) 在 CMakeLists.txt添加命令,生成除法动态库 libdivision.dll

# 生成 division 动态库
add_library(division SHARED division.cpp)

(4) build

在这里插入图片描述
(5) 把动态库文件 libdivision.dll和头文件 division.h移动到 other_lib文件夹下面。

在这里插入图片描述
(6) 在 demo/src/CMakeLists.txt添加命令,链接 libdivison.dll动态库。

target_link_libraries(demo PUBLIC G:/demo/other_lib/libdivision.dll)

(7) 在 main.cpp中调用 divide(int,int)函数。

#include<iostream>

#include "foo.h"
#include "addition.h"
#include "subtract.h"
#include "multiply.h"
#include "division.h"

using namespace std;

int main(){

    cout<<"Hello World!!!"<<endl;

    //打印 foo.h 中定义的这个宏
    cout<<"FOO_STRING : "<<FOO_STRING<<endl;

    cout<<"10 + 20 = "<<add(10,20)<<endl;

    cout<<"10 - 20 = "<<sub(10,20)<<endl;

    cout<<"10 * 20 = "<<mul(10,20)<<endl;

    cout<<"20 / 10 = "<<divide(20,10)<<endl;
    
    return 0;
}

(8) 编译、运行。

在这里插入图片描述
这时报错了,显示找不到 libdivision.dll文件。

解决方法:把 libdivision.dll复制到 demo.exe同文件夹下面。

在这里插入图片描述
此时再编译运行就可以了。

在这里插入图片描述

五、安装

把可执行文件、头文件、动态静态库安装到指定位置。

如果我们不做任何设置,默认安装,就会安装到 "C:/Program Files (x86)/demo"这个位置下。

build/cmake_install.cmake文件下可以看到默认位置。

在这里插入图片描述

(1) 安装在 src/calc目录下生成的库文件 和 头文件。

src/calc/CMakeLists.txt

#安装库文件
install(TARGETS addition DESTINATION lib)
install(TARGETS subtract DESTINATION bin)

#安装头文件
install(FILES addition.h DESTINATION include)
install(FILES subtract.h DESTINATION include)

(2) 安装在 src目录下生成的库文件 和 头文件。

src/CMakeLists.txt

#安装

#安装可执行文件,可执行文件安装到 bin 目录下
install(TARGETS demo DESTINATION bin)

#安装动态静态库;动态库安装到 bin 目录下,静态库安装到 lib 目录下
install(FILES "G:/demo/other_lib/libmultiply.a" DESTINATION lib)

install(FILES "G:/demo/other_lib/libdivision.dll" DESTINATION bin)

#安装头文件,头文件都安装到 include 目录下
install(FILES "G:/demo/other_lib/multiply.h" DESTINATION include)
install(FILES "G:/demo/other_lib/division.h" DESTINATION include)

(3) 安装到 build/install目录下,install文件夹会被创建出来。

先把 build这个文件夹的内容删除,再重新构建。

在这里插入图片描述

编译、安装

安装的命令:

#安装到 demo/install 目录下 , intall目录会自动创建
cmake --install . --prefix ..\install

在这里插入图片描述

(4) 验证是否安装成功

进入到 demo/intall/bin目录下。

在这里插入图片描述
打开 cmd,执行 demo.exe

在这里插入图片描述
执行成功,说明安装成功了!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值