C/C++跨平台构建工具CMake-----在C++源码中读取CMakeLists.txt配置文件中的内容

1.需求描述

当我们开发软件项目时,通常会用到版本控制,每个版本都会有不同的修改,因为软件一旦发布给用户,当我们升级版本的时候,必定会出现一些用户的版本会是旧版本,所以每次发布版本的时候都会有一个版本号标识用户的版本,这个版本号一般都是放到构建的配置文件中,比如Android是放到App目录下的build.gradle文件中,而我们使用CMake工具构建的C/C++的项目中,自然是放在CMakeLists.txt配置文件中啦。在CMakeLists.txt中,会用下面的语句声明版本号:

shell 复制代码
project(Tutorial VERSION 2.11)

这个版本号假设我们想要在我们的C/C++源码中读取出来,并且上传到服务器做埋点标识。直接读取肯定是不行的,因为CMakeLists.txt和C/C++源文件是属于不同的系统。那么需要如何做呢。本文就是介绍如何从CMakeLists.txt中读取我们设置的值(不只是版本号哦),并且能在C/C++源文件中访问。

2.需求准备

2.1 创建项目

创建一个C/C++演示项目,非常简单,找到一个目录,在目录下新建一个文件夹,并按照下图创建号对应的文件: 目录中包含一个build目录,用于存放构建后的产物,一个构建脚本CmakeLists.txt,一个用于读取构建脚本中值的C++源代码文件:readConfigValue.cpp,然后我们使用IDE打开这个目录,也可以直接编写,反正怎么方便怎么来吧,我使用VScode打开。

2.2 编辑CMakeLists.txt文件

在CMakeLists.txt文件下编写下面的语句代码:

clike 复制代码
#1.设置最小要求的Cmake版本号
cmake_minimum_required(VERSION 3.10)

#2.设置项目名称
project(ReadConfigValue)

#3.设置项目的版本号,在C++源文件中会读取这个版本号
project(ReadConfigValue VERSION 1.0)

#4.设置C++ 的版本,这里选择的是C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

#5.将项目编译成一个可执行文件,Windows上的 .exe文件
add_executable(ReadConfigValue readConfigValue.cpp)

这里只做了基本的配置,主要是先验证下项目是否跑通,后面还会编辑这个文件,完成我们的最终需求。

2.3 编写C++文件

在ReadConfigValue.cpp文件中编写下面的代码:

cpp 复制代码
#include<iostream>
#include<string>

int main(int argc,char* argv[]){
    std::cout<<"Hello CMake"<<std::endl;
    return 0;
}

2.4 编译构建项目

进入我们创建的项目目录下,进入build目录,打开命令行工具CMD,输入命令: cmake ..编译项目, 然后输入命令:cmake --build .构建项目 执行完上面的命令后会在build目录下生成一些编译后的文件和可执行文件: 我们在命令行执行exe文件后输出我们在C++ 源码中输出的信息就证明我们的环境准备好了,示例项目的输出为: Hello CMake

3.需求实现

项目环境准备好后,我们可以开始实现我们的需求了,其实我们熟悉Android Gradle构建脚本的小伙伴可能会注意到,在编写Android程序的时候,我们可以使用一个类叫BuildConfig,这个类就是在编译期间由Android提供的Gradle插件生成的。这里的CMakeLists.txt实现也和Gradle的方法差不多,这里的大致思想是在编译的时候,将我们想给到C++源码中的值放到一个生成的头文件里面,在C++程序中引用这个头文件就可以了。接下来我们看下具体实现:

3.1 在CMakeLists.txt中输出日志信息

在编写CMakeLists.txt文件时,我们常常需要打印一些信息,这里我们使用一个函数:message(STATUS 内容),如下所示:

cpp 复制代码
#1.设置最小要求的Cmake版本号
cmake_minimum_required(VERSION 3.10)

#2.设置项目名称
project(ReadConfigValue)

#3.设置项目的版本号,在C++源文件中会读取这个版本号
project(ReadConfigValue VERSION 1.0)

#4.设置C++ 的版本,这里选择的是C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

#5.将项目编译成一个可执行文件,Windows上的 .exe文件
add_executable(ReadConfigValue readConfigValue.cpp)

#6.打印调试信息
message(STATUS "Hello from CMakeLists.txt")
message(STATUS "${PROJECT_BINARY_DIR}")
message(STATUS "${ReadConfigValue_VERSION_MAJOR}")
message(STATUS "${ReadConfigValue_VERSION_MINOR}")

修改完CMakeLists.txt后,进入项目的build目录,输入命令cmake . 会得到下图中的信息:

3.2 增加配置生成C++头文件

我们如果要实现在C++中读取到CMakeLists.txt中的值,需要先生成头文件。生成头文件我们需要先在根目录下创建一个config.h.in文件,用于配置生成的头文件信息:创建好的文件如下图所示: 然后在CMakeLists.txt文件中添加代码引用我们新创建的配置文件,代码如下所示:

cpp 复制代码
configure_file(config.h.in config.h)

并且在我们创建的config.h.in中配置我们要给C++访问的值,如下所示:

cpp 复制代码
#define ReadConfigValue_VERSION_MAJOR ${ReadConfigValue_VERSION_MAJOR}
// 可以使用@ 或者${}去获取对应的值
#define ReadConfigValue_VERSION_MINOR @ReadConfigValue_VERSION_MINOR@

3.3在C++ 源码中访问配置的值

编写完上面的配置后,我们在readConfigValue.cpp文件中引用config.h头文件

cpp 复制代码
#include<iostream>
#include<string>
#include "config.h"

int main(int argc,char* argv[]){
    std::cout<<"Hello CMake"<<std::endl;
    std::cout<<"Major version=>"<<ReadConfigValue_VERSION_MAJOR<<std::endl;
    std::cout<<"Major version=>"<<ReadConfigValue_VERSION_MINOR<<std::endl;
    return 0;
}

然后我们编译后会得到一个config.h头文件: 头文件中的内容就是我们在CMakeLists.txt文件中配置的值:

但是当我们构建时会报错: 原因就是我们的config.h文件生成成功了,但是没有正确的引用到C++源文件中,也就是说include"config.h"找不到config.h的路径,所以我们需要在CMakeLists.txt文件中配置好这个路径,代码如下所示:

cpp 复制代码
target_include_directories(ReadConfigValue PUBLIC "${PROJECT_BINARY_DIR}")

这里的第一个参数是我们的项目名称,第二个参数可以是PUBLIC、PRIVATE、INTERFACE 目前暂时使用PUBLIC就行,最后一个是我们生成的config.h所在目录的路径,我们的config.h实际上是在build目录下的,我们在前面输出的调试信息中也发现${PROJECT_BINARY_DIR}输出的是build目录的路径,所以配置好它就行了

配置完成后我们再编译构建就发现可以运行并且成功读取到CMakeLists.txt文件的值了。

3.4 C++文件中读取CMakeLists.txt中的字符串

如果我们想要读取CMakeLists.txt文件中的字符串也是可以的,比较简单,首先在CMakeLists.txt中设置我们要给C++源代码文件中读取的字符串:

cpp 复制代码
#.设置字符串给C++文件读取
set(STR_VALUE "I am String from CMakeLists.txt")

如上图所示,需要注意set()语句的位置,不能放在最后否则这个值无法生成 然后再config.h.in中增加配置:

cpp 复制代码
//必须使用双引号,否则在C++源码读取的时候这里没有双引号包裹,会导致读取错误
#define STR_VALUE "@STR_VALUE@"

注意:配置字符串时必须使用双引号包裹我们的取值语句,否则在C++源码读取的时候这里没有双引号包裹,会导致读取错误 然后在C++文件中访问:

cpp 复制代码
#include<iostream>
#include<string>
#include "config.h"

int main(int argc,char* argv[]){
    std::cout<<"Hello CMake"<<std::endl;
    std::cout<<"Major version=>"<<ReadConfigValue_VERSION_MAJOR<<std::endl;
    std::cout<<"Major version=>"<<ReadConfigValue_VERSION_MINOR<<std::endl;
    std::cout<<"String value from CMakeLists.txt==>"<<STR_VALUE<<std::endl;
    return 0;
}

最后编译运行:

总结

本文虽然简单,但是在开发中确实有用,比如我们的程序中想要区分debug环境和release环境的时候就可以在CMakeList中添加配置,就像Android 的gradle 插件生成的BuildCongfig类一样,我们可以方便的用这个类的DEBUG和RELEASE来区分开发环境和正式环境,以此来隔离掉一些开发环境的log。所以建议小伙伴们熟悉这种使用方法。对开发会很有用哦。

相关推荐
dmy2 小时前
n8n内网快速部署
运维·人工智能·程序员
憨憨睡不醒啊4 小时前
如何让LLM智能体开发助力求职之路——构建属于你的智能体开发知识体系📚📚📚
面试·程序员·llm
程序员岳焱5 小时前
Java 程序员成长记(二):菜鸟入职之 MyBatis XML「陷阱」
java·后端·程序员
liangdabiao7 小时前
让AI写出真正可用的图文并茂的帖子(微信公众号,小红书,博客)
程序员
安妮的心动录7 小时前
人是习惯的结果
面试·程序员·求职
小兵张健8 小时前
笔记本清灰记录
程序员
陈随易10 小时前
Univer v0.8.0 发布,开源免费版 Google Sheets
前端·后端·程序员
陈随易1 天前
Element Plus 2.10.0 重磅发布!新增Splitter组件
前端·后端·程序员
陈随易1 天前
2025年100个产品计划之第11个(哆啦工具箱) - 像哆啦A梦口袋一样丰富的工具箱
前端·后端·程序员
大模型教程1 天前
RAG 实战指南(五):RAG 信息检索:如何让模型找到‘对的知识’
程序员·llm