VS2019 编译几个开源软件

编译常用的几个开源软件

Posted by Jerry Chen on January 1, 2021

下面主要介绍 VS2019 下单独编译库,以及在 Windows 平台构建测试工程,在多平台上验证;

libuv 库

单独编译库

下载地址:github

方法见:Windows 上编译 libuv 库

FetchContent 方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───cmake			# cmake 模块脚本目录,存放 xxx.cmake 文件
		libuv2.cmake
├───ext				# 第三方库(如 libuv)存放目录,cmake 缓存时下载
├───include			# 头文件路径,当前为空
└───src
		main.cpp	# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

int main(int argc, char *argv[])
{
    uv_loop_t *loop = (uv_loop_t *)malloc(sizeof(uv_loop_t));
    uv_loop_init(loop);

    printf("Now quitting.\n");
    uv_run(loop, UV_RUN_DEFAULT);

    uv_loop_close(loop);
    free(loop);
    return 0;
}

libuv2.cmake

fetchcontent_makeavailable 宏在 cmake 3.14 才得到支持;

1
2
3
4
5
6
7
8
9
10
# 指定远程仓库地址、版本,本地存放地址
# 如果 github 太慢,可用 gitee 代替
fetchcontent_declare(uv
	GIT_REPOSITORY https://github.com/libuv/libuv.git
	GIT_TAG v1.40.0
	SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/libuv
)
# 设置目标 CMakeLists.txt 中的选项,不编译库的附加程序
set(LIBUV_BUILD_TESTS OFF CACHE INTERNAL "")
fetchcontent_makeavailable(uv)

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(libuv_demo CXX)

# 使用 fetchcontent
include(FetchContent)

# 设置版本为 c++11
set(CMAKE_CXX_STANDARD 11)

# 创建可执行程序项目
add_executable(libuv_demo src/main.cpp)

# 设定模块路径:当前目录 cmake 路径以及原本的模块路径
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")

# 引入 libuv2.camake(fetchcontent 方式)
include(libuv2)

# 链接库,使用 libuv
target_link_libraries(libuv_demo PRIVATE uv)

# 构建成功后复制依赖库到可执行程序所在目录
if (WIN32)
    add_custom_command(TARGET libuv_demo POST_BUILD 
	    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/_deps/uv-build/uv.dll ${CMAKE_BINARY_DIR}
    )
else ()
    add_custom_command(TARGET libuv_demo POST_BUILD 
	    COMMAND cp ${CMAKE_BINARY_DIR}/_deps/uv-build/libuv.so ${CMAKE_BINARY_DIR}/libuv.so.1
    )
endif ()

Windows 使用结果:

Linux 使用结果:

spdlog 库

单独编译库

下载地址:github

  1. 使用 VS2019 打开 spdlog 目录下的 CMakeLists.txt;

    会在工程目录下多出 .vs 和 out 目录;

    打开工程后,会自动按 x64-Debug 默认配置生成 cmake 缓存,默认路径在 out\build\${cfg_name} 目录,然后 C++ IntelliSense 也会按照 cmake 缓存进行构建;

  2. 在解决方案资源管理器的 CMakeLists.txt 上右键 “CMake 设置”,新增一个 x64-Release 配置并保存;

    会在工程目录下多出 CMakeSettings.json 文件;

  3. 选择 x64-Reelease 配置;

  4. 接着在解决方案资源管理器的 CMakeLists.txt 上右键 “生成”

    最终在 out\build\x64-Release 目录下会生成我们的编译目标:

FetchContent 方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───cmake			# cmake 模块脚本目录,存放 xxx.cmake 文件
		spdlog2.cmake
├───ext				# 第三方库(如 spdlog)存放目录,cmake 缓存时下载
├───include			# 头文件路径,当前为空
└───src
		main.cpp	# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.cpp

1
2
3
4
5
6
7
#include <spdlog/spdlog.h>

int main(int argc, char *argv[])
{
	spdlog::info("I love c++");
	return 0;
}

spdlog2.cmake

fetchcontent_makeavailable 宏在 cmake 3.14 才得到支持;

1
2
3
4
5
6
7
8
9
10
# 指定远程仓库地址、版本,本地存放地址
# 如果 github 太慢,可用 gitee 代替
fetchcontent_declare(spdlog
	GIT_REPOSITORY https://github.com/gabime/spdlog.git
	GIT_TAG v1.8.2
	SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/spdlog
)
# 设置目标 CMakeLists.txt 中的选项,不编译库的附加程序
set(SPDLOG_BUILD_EXAMPLE OFF CACHE INTERNAL "")
fetchcontent_makeavailable(spdlog)

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(spdlog_demo CXX)

# 使用 fetchcontent
include(FetchContent)

# spdlog 模块需要 c++11 及以上版本
set(CMAKE_CXX_STANDARD 11)

# 创建可执行程序项目
add_executable(spdlog_demo src/main.cpp)

# 设定模块路径:当前目录 cmake 路径以及原本的模块路径
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")

# 引入 spdlog2.camake(fetchcontent 方式)
include(spdlog2)

# 链接库,使用 spdlog
target_link_libraries(spdlog_demo PRIVATE spdlog::spdlog)

Windows 使用结果:

Linux 使用结果:

simpleini 库

编译

下载地址:github

simpleini 不需要编译为库文件,使用头文件即可;

使用

工程文件结构如下:

1
2
3
4
5
6
7
8
9
10
11
.
├───out				# VS 的 cmake 工程输出目录
├───ext				# 第三方库(如 SimpleIni)存放目录
	└───SimpleIni
			SimpleIni.h
			ConvertUTF.h
			ConvertUTF.c
├───include			# 头文件路径,当前为空
└───src
		main.cpp	# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include "SimpleIni.h"

int main(int argc, char *argv[])
{
    CSimpleIniA ini;
    ini.SetUnicode();
    
    SI_Error rc = ini.LoadFile("myfile.ini");
	if (rc < 0) {
        printf("Can't load \"myfile.ini\"\n");
        return -1;
    }
    
    const char *pVal = ini.GetValue("section", "key", "default");
    printf("Key is %s\n", pVal);
    
    ini.SetValue("section", "key", "newvalue");
    ini.SaveFile("myfile.ini");
    return 0;
}

CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(simpleini_demo CXX)

# 设置版本为 c++11
set(CMAKE_CXX_STANDARD 11)

# 头文件路径
message("include dir: " ${PROJECT_SOURCE_DIR}/ext/SimpleIni)
include_directories(${PROJECT_SOURCE_DIR}/ext/SimpleIni)

# 指定 SimpleIni 库的源文件
set(simpleini_src_file 
	ext/SimpleIni/ConvertUTF.c
)

# 生成可执行文件
add_executable(simpleini_demo src/main.cpp ${simpleini_src_file})

测试使用的 myfile.ini 如下:

1
2
[section]
key = abc

Windows 使用结果:

Linux 使用结果:

pugixml 库

单独编译库

下载地址:github

  1. 使用 VS2019 打开 pugixml 目录下的 CMakeLists.txt;

    会在工程目录下多出 .vs 和 out 目录;

    打开工程后,会自动按 x64-Debug 默认配置生成 cmake 缓存,默认路径在 out\build\${cfg_name} 目录,然后 C++ IntelliSense 也会按照 cmake 缓存进行构建;

  2. 在解决方案资源管理器的 CMakeLists.txt 上右键 “CMake 设置”,新增一个 x64-Release 配置并保存;

    会在工程目录下多出 CMakeSettings.json 文件;

  3. 选择 x64-Reelease 配置;

  4. 接着在解决方案资源管理器的 CMakeLists.txt 上右键 “生成”

    最终在 out\build\x64-Release 目录下会生成我们的编译目标:

FetchContent 方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───cmake			# cmake 模块脚本目录,存放 xxx.cmake 文件
		pugixml2.cmake
├───ext				# 第三方库(如 pugixml)存放目录,cmake 缓存时下载
├───include			# 头文件路径,当前为空
└───src
		main.cpp	# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include "pugixml.hpp"

int main(int argc, char *argv[])
{
    using namespace pugi;

    xml_document doc;
    xml_parse_result result = doc.load_file("myfile.xml");
    if (result.status != status_ok){
        printf("myfile.xml has an error XML format\n");
    }

    if (!doc.child("root"))
        doc.append_child("root");

    xml_node node = doc.child("root").child("ip");
    if (!node)
        doc.child("root").append_child("ip");

    node.text().set("10.10.10.10");
    doc.save_file("myfile.xml");
    return 0;
}

pugixml2.cmake

fetchcontent_makeavailable 宏在 cmake 3.14 才得到支持;

1
2
3
4
5
6
7
8
# 指定远程仓库地址、版本,本地存放地址
# 如果 github 太慢,可用 gitee 代替
fetchcontent_declare(pugixml
	GIT_REPOSITORY https://github.com/zeux/pugixml.git
	GIT_TAG v1.11.4
	SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/pugixml
)
fetchcontent_makeavailable(pugixml)

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(pugixml_demo CXX)

# 使用 fetchcontent
include(FetchContent)

# 设置版本为 c++11
set(CMAKE_CXX_STANDARD 11)

# 创建可执行程序项目
add_executable(pugixml_demo src/main.cpp)

# 设定模块路径:当前目录 cmake 路径以及原本的模块路径
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")

# 引入 pugixml2.camake(fetchcontent 方式)
include(pugixml2)

# 链接库,使用 pugixml::pugixml
target_link_libraries(pugixml_demo PRIVATE pugixml::pugixml)

测试使用的 myfile.xml 如下:

1
2
3
4
<?xml version="1.0"?>
<root>
	<ip>192.168.1.1</ip>
</root>

Windows 使用结果,myfile.xml 文件被更新:

Linux 使用结果:

yaml-cpp 库

单独编译库

下载地址:github

  1. 使用 VS2019 打开 yaml-cpp 目录下的 CMakeLists.txt;

    会在工程目录下多出 .vs 和 out 目录;

    打开工程后,会自动按 x64-Debug 默认配置生成 cmake 缓存,默认路径在 out\build\${cfg_name} 目录,然后 C++ IntelliSense 也会按照 cmake 缓存进行构建;

  2. 在解决方案资源管理器的 CMakeLists.txt 上右键 “CMake 设置”,新增一个 x64-Release 配置并保存;

    会在工程目录下多出 CMakeSettings.json 文件;

  3. 选择 x64-Reelease 配置;

  4. 接着在解决方案资源管理器的 CMakeLists.txt 上右键 “生成”

    最终在 out\build\x64-Release 目录下会生成我们的编译目标:

FetchContent 方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───cmake			# cmake 模块脚本目录,存放 xxx.cmake 文件
		yaml-cpp2.cmake
├───ext				# 第三方库(如 yaml-cpp)存放目录,cmake 缓存时下载
├───include			# 头文件路径,当前为空
└───src
		main.cpp	# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "yaml-cpp/yaml.h"
#include <iostream>

int main(int argc, char *argv[])
{
    YAML::Node root = YAML::LoadFile("myfile.yaml");

    /****************************
    - foo
    - primes: [2, 3, 5, 7, 11]
      odds: [1, 3, 5, 7, 9, 11]
    - [x, y]
    ****************************/

    std::cout << "myfile.yaml:" << std::endl;
    std::cout << root << std::endl;
    std::cout << std::endl;

    root[2].push_back(5);
    root[3] = "Hello, World";

    std::cout << root[0] << std::endl;              // "foo"
    std::cout << root[1]["primes"] << std::endl;    // "[2, 3, 5, 7, 11]"
    std::cout << root[1]["primes"][3] << std::endl; // "7"
    std::cout << root[2] << std::endl;              // "[x, y]"
    std::cout << root[3] << std::endl;              // "Hello, World"

    std::string s = root[0].as<std::string>();
    printf("s is %s\n", s.c_str());
    int i = root[1]["primes"][3].as<int>();
    printf("i is %d\n", i);

    return 0;
}

yaml-cpp2.cmake

fetchcontent_makeavailable 宏在 cmake 3.14 才得到支持;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 指定远程仓库地址、版本,本地存放地址
# 如果 github 太慢,可用 gitee 代替
fetchcontent_declare(yaml
	GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
	GIT_TAG yaml-cpp-0.6.3
	SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/yaml-cpp
)
# 设置目标 CMakeLists.txt 中的选项,不编译库的附加程序
set(YAML_CPP_BUILD_TESTS OFF CACHE INTERNAL "")
set(YAML_CPP_BUILD_TESTS OFF CACHE INTERNAL "")
set(YAML_CPP_BUILD_CONTRIB OFF CACHE INTERNAL "")
set(YAML_CPP_BUILD_TOOLS OFF CACHE INTERNAL "")
set(YAML_CPP_INSTALL OFF CACHE INTERNAL "")
fetchcontent_makeavailable(yaml)

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(yaml_demo CXX)

# 使用 fetchcontent
include(FetchContent)

# 设置版本为 c++11
set(CMAKE_CXX_STANDARD 11)

# 创建可执行程序项目
add_executable(yaml_demo src/main.cpp)

# 设定模块路径:当前目录 cmake 路径以及原本的模块路径
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")

# 引入 yaml-cpp2.camake(fetchcontent 方式)
include(yaml-cpp2)

# 链接库,使用 yaml-cpp
target_link_libraries(yaml_demo PRIVATE yaml-cpp)

测试使用的 myfile.yaml 如下:

1
2
3
4
- foo
- primes: [2, 3, 5, 7, 11]
  odds: [1, 3, 5, 7, 9, 11]
- [x, y]

Windows 使用结果:

Linux 使用结果:

nlohmann_json 库

编译

下载地址:github

nlohmann_json 只有单个头文件;

FetchContent 方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───cmake			# cmake 模块脚本目录,存放 xxx.cmake 文件
		nlohmann_json2.cmake
├───ext				# 第三方库(如 nlohmann_json)存放目录,cmake 缓存时下载
├───include			# 头文件路径,当前为空
└───src
		main.cpp	# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include "nlohmann/json.hpp"
using json = nlohmann::json;

int main(int argc, char *argv[])
{
    json j;
    j.push_back("Hello");
    j.emplace_back("World");
    j += false;
    j[2] = 42.3;
    j[4] = "World";
    
    std::cout << j << std::endl;
}

nlohmann_json2.cmake

fetchcontent_makeavailable 宏在 cmake 3.14 才得到支持;

1
2
3
4
5
6
7
8
9
10
# 指定远程仓库地址、版本,本地存放地址
# 如果 github 太慢,可用 gitee 代替
fetchcontent_declare(json
	GIT_REPOSITORY https://github.com/nlohmann/json.git
	GIT_TAG v3.9.1
	SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/nlohmann_json
)
# 设置目标 CMakeLists.txt 中的变量,不编译库的测试程序
set(JSON_BuildTests OFF CACHE INTERNAL "")
fetchcontent_makeavailable(json)

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(json_demo CXX)

# 使用 fetchcontent
include(FetchContent)

# 设置版本为 c++11
set(CMAKE_CXX_STANDARD 11)

# 创建可执行程序项目
add_executable(json_demo src/main.cpp)

# 设定模块路径:当前目录 cmake 路径以及原本的模块路径
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")

# 引入 nlohmann_json2.camake(fetchcontent 方式)
include(nlohmann_json2)

# 链接库,使用 nlohmann_json::nlohmann_json
target_link_libraries(json_demo PRIVATE nlohmann_json::nlohmann_json)

Windows 使用结果:

Linux 使用结果:

mosquitto 库

单独编译库

下载地址:官网

CMakeLists.txt 需要先配置下 option,否则编译不过,可以在 cmake 时传参也可以将下面的关闭配置放在脚本上方:

不建议按如下设置,新的编译步骤可以参考我的最新文档;

1
2
3
4
5
6
7
8
9
10
set(WITH_TLS OFF CACHE INTERNAL "")
set(WITH_UNIX_SOCKETS OFF CACHE INTERNAL "")
set(WITH_SOCKS OFF CACHE INTERNAL "")
set(WITH_THREADING OFF CACHE INTERNAL "")
set(WITH_LIB_CPP OFF CACHE INTERNAL "")
set(WITH_CLIENTS OFF CACHE INTERNAL "")
set(WITH_BROKER OFF CACHE INTERNAL "")
set(WITH_APPS OFF CACHE INTERNAL "")
set(WITH_PLUGINS OFF CACHE INTERNAL "")
set(DOCUMENTATION OFF CACHE INTERNAL "")
  1. 使用 VS2019 打开 CMake 工程;

  2. 接着在解决方案资源管理器的 CMakeLists.txt 上右键 “生成”,最终在 out\build\x64-Release\lib 目录下会生成我们的编译目标;

子工程方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───ext				# 第三方库(如 mosquitto)存放目录,cmake 缓存时下载
	└───mosquitto	# mosquitto 库目录
			xxx		# mosquitto 库中文件
├───include			# 头文件路径,当前为空
└───src
		main.c		# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mosquitto.h"

#define HOST "localhost"
#define PORT  1883
#define KEEP_ALIVE 60
#define MSG_MAX_SIZE  512

// 定义运行标志决定是否需要结束
static int running = 1;

void my_connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
	printf("Call the function: on_connect\n");

	if(rc){
		// 连接错误,退出程序
		printf("on_connect error!\n");
		exit(1);
	}else{
		// 订阅主题
		// 参数:句柄、id、订阅的主题、qos
		if(mosquitto_subscribe(mosq, NULL, "topic1", 2)){
			printf("Set the topic error!\n");
			exit(1);
		}
	}
}

void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc)
{
	printf("Call the function: my_disconnect_callback\n");
	running = 0;
}

void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
	printf("Call the function: on_subscribe\n");
}

void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
	printf("Call the function: on_message\n");
	printf("Recieve a message of %s : %s\n", (char *)msg->topic, (char *)msg->payload);

	if(0 == strcmp(msg->payload, "quit")){
		mosquitto_disconnect(mosq);
	}
}

int main(int argc, char *argv[])
{
	int ret;
	struct mosquitto *mosq;

	// 初始化mosquitto库
	ret = mosquitto_lib_init();
	if(ret){
		printf("Init lib error!\n");
		return -1;
	}

	// 创建一个订阅端实例
	// 参数:id(不需要则为NULL)、clean_start、用户数据
	mosq =  mosquitto_new("sub_test", true, NULL);
	if(mosq == NULL){
		printf("New sub_test error!\n");
		mosquitto_lib_cleanup();
		return -1;
	}

	// 设置回调函数
	// 参数:句柄、回调函数
	mosquitto_connect_callback_set(mosq, my_connect_callback);
	mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
	mosquitto_subscribe_callback_set(mosq, my_subscribe_callback);
	mosquitto_message_callback_set(mosq, my_message_callback);

	// 连接至服务器
	// 参数:句柄、ip(host)、端口、心跳
	ret = mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE);
	if(ret){
		printf("Connect server error!\n");
		mosquitto_destroy(mosq);
		mosquitto_lib_cleanup();
		return -1;
	}

	 // 开始通信:循环执行、直到运行标志running被改变
	printf("Start!\n");
	while(running)
	{
		mosquitto_loop(mosq, -1, 1);
	}

	// 结束后的清理工作
	mosquitto_destroy(mosq);
	mosquitto_lib_cleanup();
	printf("End!\n");

	return 0;
}

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(mqtt_demo)

set(WITH_TLS OFF CACHE INTERNAL "")
set(WITH_UNIX_SOCKETS OFF CACHE INTERNAL "")
set(WITH_SOCKS OFF CACHE INTERNAL "")
set(WITH_THREADING OFF CACHE INTERNAL "")
set(WITH_LIB_CPP OFF CACHE INTERNAL "")
set(WITH_CLIENTS OFF CACHE INTERNAL "")
set(WITH_BROKER OFF CACHE INTERNAL "")
set(WITH_APPS OFF CACHE INTERNAL "")
set(WITH_PLUGINS OFF CACHE INTERNAL "")
set(DOCUMENTATION OFF CACHE INTERNAL "")
include_directories(ext/mosquitto/include)
add_subdirectory(ext/mosquitto)

# 创建可执行程序项目
add_executable(mqtt_demo src/main.c)

# 链接库,使用 libmosquitto
target_link_libraries(mqtt_demo PRIVATE libmosquitto)

# 构建成功后复制依赖库到可执行程序所在目录
if (WIN32)
    add_custom_command(TARGET mqtt_demo POST_BUILD 
	    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/ext/mosquitto/lib/mosquitto.dll ${CMAKE_BINARY_DIR}
    )
else ()
    add_custom_command(TARGET mqtt_demo POST_BUILD 
	    COMMAND cp ${CMAKE_BINARY_DIR}/ext/mosquitto/lib/libmosquitto.so ${CMAKE_BINARY_DIR}/libmosquitto.so.1
    )
endif ()

Windows 使用结果:

Linux 使用结果:

FetchContent 方式包含并使用库

工程文件结构如下:

1
2
3
4
5
6
7
8
9
.
├───out				# VS 的 cmake 工程输出目录
├───cmake			# cmake 模块脚本目录,存放 xxx.cmake 文件
		mosquitto2.cmake
├───ext				# 第三方库(如 mosquitto)存放目录,cmake 缓存时下载
├───include			# 头文件路径,当前为空
└───src
		main.c		# 主项目源文件
└───CMakeLists.txt	# 顶层目录的 cmake 脚本

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mosquitto.h"

#define HOST "localhost"
#define PORT  1883
#define KEEP_ALIVE 60
#define MSG_MAX_SIZE  512

// 定义运行标志决定是否需要结束
static int running = 1;

void my_connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
	printf("Call the function: on_connect\n");

	if(rc){
		// 连接错误,退出程序
		printf("on_connect error!\n");
		exit(1);
	}else{
		// 订阅主题
		// 参数:句柄、id、订阅的主题、qos
		if(mosquitto_subscribe(mosq, NULL, "topic1", 2)){
			printf("Set the topic error!\n");
			exit(1);
		}
	}
}

void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc)
{
	printf("Call the function: my_disconnect_callback\n");
	running = 0;
}

void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
	printf("Call the function: on_subscribe\n");
}

void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
	printf("Call the function: on_message\n");
	printf("Recieve a message of %s : %s\n", (char *)msg->topic, (char *)msg->payload);

	if(0 == strcmp(msg->payload, "quit")){
		mosquitto_disconnect(mosq);
	}
}

int main(int argc, char *argv[])
{
	int ret;
	struct mosquitto *mosq;

	// 初始化mosquitto库
	ret = mosquitto_lib_init();
	if(ret){
		printf("Init lib error!\n");
		return -1;
	}

	// 创建一个订阅端实例
	// 参数:id(不需要则为NULL)、clean_start、用户数据
	mosq =  mosquitto_new("sub_test", true, NULL);
	if(mosq == NULL){
		printf("New sub_test error!\n");
		mosquitto_lib_cleanup();
		return -1;
	}

	// 设置回调函数
	// 参数:句柄、回调函数
	mosquitto_connect_callback_set(mosq, my_connect_callback);
	mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
	mosquitto_subscribe_callback_set(mosq, my_subscribe_callback);
	mosquitto_message_callback_set(mosq, my_message_callback);

	// 连接至服务器
	// 参数:句柄、ip(host)、端口、心跳
	ret = mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE);
	if(ret){
		printf("Connect server error!\n");
		mosquitto_destroy(mosq);
		mosquitto_lib_cleanup();
		return -1;
	}

	 // 开始通信:循环执行、直到运行标志running被改变
	printf("Start!\n");
	while(running)
	{
		mosquitto_loop(mosq, -1, 1);
	}

	// 结束后的清理工作
	mosquitto_destroy(mosq);
	mosquitto_lib_cleanup();
	printf("End!\n");

	return 0;
}

mosquitto2.cmake

fetchcontent_makeavailable 宏在 cmake 3.14 才得到支持;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 指定远程仓库地址、版本,本地存放地址
# 如果 github 太慢,可用 gitee 代替
fetchcontent_declare(mqtt
	GIT_REPOSITORY https://github.com/eclipse/mosquitto.git
	GIT_TAG v2.0.4
	SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/mosquitto
)
# 设置目标 CMakeLists.txt 中的变量,不编译库的测试程序
set(WITH_TLS OFF CACHE INTERNAL "")
set(WITH_UNIX_SOCKETS OFF CACHE INTERNAL "")
set(WITH_SOCKS OFF CACHE INTERNAL "")
set(WITH_THREADING OFF CACHE INTERNAL "")
set(WITH_LIB_CPP OFF CACHE INTERNAL "")
set(WITH_CLIENTS OFF CACHE INTERNAL "")
set(WITH_BROKER OFF CACHE INTERNAL "")
set(WITH_APPS OFF CACHE INTERNAL "")
set(WITH_PLUGINS OFF CACHE INTERNAL "")
set(DOCUMENTATION OFF CACHE INTERNAL "")
fetchcontent_makeavailable(mqtt)
include_directories(ext/mosquitto/include)

工程目录下 CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 设置 CMake 最低可用版本
cmake_minimum_required(VERSION 3.14)

# 设置工程名称和其他属性
project(mqtt_demo)

# 使用 fetchcontent
include(FetchContent)

# 创建可执行程序项目
add_executable(mqtt_demo src/main.c)

# 设定模块路径:当前目录 cmake 路径以及原本的模块路径
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")

# 引入 mosquitto2.camake(fetchcontent 方式)
include(mosquitto2)

# 链接库,使用 libmosquitto
target_link_libraries(mqtt_demo PRIVATE libmosquitto)

# 构建成功后复制依赖库到可执行程序所在目录
if (WIN32)
    add_custom_command(TARGET mqtt_demo POST_BUILD 
	    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/_deps/mqtt-build/lib/mosquitto.dll ${CMAKE_BINARY_DIR}
    )
else ()
    add_custom_command(TARGET mqtt_demo POST_BUILD 
	    COMMAND cp ${CMAKE_BINARY_DIR}/_deps/mqtt-build/lib/libmosquitto.so ${CMAKE_BINARY_DIR}/libmosquitto.so.1
    )
endif ()

Windows 使用结果:

Linux 使用结果: