xmake中将proto文件生成C/C++文件然后再编译链接进项目

前几天在玩CMake的时候想让CMake来生成pkgconfig的配置文件pc文件,尝试了很久都没成功,借助DeepSeek也没搞出来。于是研究起了xmake,不用不知道,一用真觉得xmake比CMake好用多了,最主要是的语法简洁,没有CMake怪异语法和晦涩的生成表达式,xmake完全使用的Lua的语言规则。还有一个非常重要的工具就是VSCode有xmake插件,可以像CMake插件一样流畅的工作。

举一下简单的示例,创建一个xmake工程,xmake create hello:

bash 复制代码
$ xmake create hello
create hello ...
  [+]: .gitignore
  [+]: src\main.cpp
  [+]: xmake.lua
create ok!

xmake.lua:

lua 复制代码
add_rules("mode.debug", "mode.release") -- 添加debug和release模式

target("hello") -- 目标为hello
    set_kind("binary") -- 生成可执行文件,如果是static为静态库,shared为动态库
    add_files("src/*.cpp") -- 添加要编译的源文件

日常项目中可能会有多目录的情况,比如一个目录为库,一个目录为依赖这个库的可执行文件:

顶层xmake.lua:

lua 复制代码
includes("mylib")
includes("app")

mylib的xmake.lua:

lua 复制代码
target("mylib")
  set_kind("static")
  add_files("*.c")
  add_includedirs(".", {public = true})

app的xmake.lua:

lua 复制代码
target("mylib")
  set_kind("binary")
  add_files("*.c")
  add_deps("mylib")

感觉入门非常容易。

如果在项目中使用了protobuf,则可以使用内置的protobuf规则,有"protobuf.c"规则,也有"protobuf.cpp"规则。

app/xmake.lua:

lua 复制代码
add_requires("libprotobuf-c") -- 查找PBC的依赖库
target("app")
	set_kind("binary")
	add_rules("protobuf.c")
	add_files("pb/*.proto", {proto_public = true}) -- 这里需要导出proto 的头文件搜索目录
	add_packages("libprotobuf-c") -- 将PBC的依赖库添加到目标

但是"protobuf.c"是查找的是protoc-c程序,可以查看xmake/rules/protobuf/proto.lua源码:

lua 复制代码
-- get protoc
function _get_protoc(target, sourcekind)

    -- find protoc
    -- @see https://github.com/xmake-io/xmake/issues/4659
    local envs = os.joinenvs(target:pkgenvs(), os.getenvs())
    local protoc = target:data("protobuf.protoc")
    if not protoc and sourcekind == "cxx" then
        protoc = find_tool("protoc", {envs = envs})
        if protoc and protoc.program then
            target:data_set("protobuf.protoc", protoc.program)
        end
    end

    -- find protoc-c
    local protoc_c = target:data("protobuf.protoc-c")
    if not protoc_c and sourcekind == "cc" then
        protoc_c = find_tool("protoc-c", {envs = envs}) or protoc
        if protoc_c and protoc_c.program then
            target:data_set("protobuf.protoc-c", protoc_c.program)
        end
    end

    -- get protoc
    return assert(target:data(sourcekind == "cxx" and "protobuf.protoc" or "protobuf.protoc-c"), "protoc not found!")
end

在MinGW下会报错:

bash 复制代码
$ xmake
error: protoc not found!

protoc-c已经过时了,官方建议使用protoc程序,它会调用protoc-gen-c来生成C代码。从上面的源码可以看到在会先使用target:data("protobuf.protoc-c")获取生成C语言文件的protoc程序,如果没有设置则查找"protoc-c",找到了再使用target:data_set("protobuf.protoc-c", protoc_c.program)设置数据。我们可以手动设置这个数据,避免查找"protoc-c"。

app/xmake.lua:

lua 复制代码
add_requires("libprotobuf-c")  -- 查找PBC的依赖库
target("app") -- 目标app
	set_kind("binary") -- 可执行文件
	add_rules("protobuf.c") -- 添加规则
	add_files("pb/*.proto", {proto_public = true})  -- 添加proto文件,这里需要导出proto 的头文件搜索目录
	add_files("*.c") -- 添加C源文件
	add_packages("libprotobuf-c")  -- 将PBC的依赖库添加到目标
	on_load(function (target)
		target:data_set("protobuf.protoc-c","protoc") -- 手动设置PB的C生成器
	end)

这样就可以正常使用了,生成C++代码不用手动设置。xmake会先使用protobuf规则将proto文件生成C/C++源文件和头文件,再编译C/C++源文件。

笔者曾经写过一篇博文:C语言使用Protobuf进行网络通信,里面介绍了如何定义宏来方便快捷的处理Protobuf的网络消息,最终为了方便宏处理,对protobuf-c库进行了一点小调整,也就是需要使用自己修改过源码生成的protoc-gen-c程序。假如自己编译生成的protoc-gen-c程序在app/pb目录中,则需要在处理proto文件前将app/pb目录的路径添加到PATH环境变量中去,才能优先使用自己的程序:

app/xmake.lua:

lua 复制代码
add_requires("libprotobuf-c")  -- 查找PBC的依赖库
target("app") -- 目标app
	set_kind("binary") -- 可执行文件
	add_rules("protobuf.c") -- 添加规则
	add_files("pb/*.proto", {proto_public = true})  -- 添加proto文件,这里需要导出proto 的头文件搜索目录
	add_files("*.c") -- 添加C源文件
	add_packages("libprotobuf-c")  -- 将PBC的依赖库添加到目标
	on_load(function (target)
		target:data_set("protobuf.protoc-c","protoc") -- 手动设置PB的C生成器
		-- 需要使用vformat手动展开变量$(scriptdir),构成绝对路径
		local custom_dir = vformat("$(scriptdir)/pb")
		-- 获取当前 PATH
		local current_path = os.getenv("PATH") or ""
		-- 根据操作系统选择路径分隔符
		local sep = (os.host() == "windows") and ";" or ":"
		-- 将自定义目录添加到 PATH 最前面(确保优先级最高)
		local new_path = custom_dir .. sep .. current_path
		-- 设置PATH变量
		os.setenv("PATH", new_path)
	end)

如本文对你有帮助,欢迎点赞收藏!

相关推荐
lhbweilai17 小时前
分析FreeRTOS内核对象API的源码实现(八)
c语言·开发语言·stm32·单片机
努力努力再努力wz17 小时前
【C++高阶系列】C++20 协程底层原理:函数为什么能暂停?从状态机到 Task,彻底串起挂起、恢复与协程调度
开发语言·c++·c++20·1024程序员节
划破黑暗的第一缕曙光17 小时前
[C++]:4.模板
c++·template·模板·函数模版·类模版
十五年专注C++开发17 小时前
pybind11:一个跨C++和Python平台的编程库
开发语言·c++·python·pybind11
艾络迅Acceleronix17 小时前
环境监测终端:STM32+EG800Z AT+ 艾络迅平台实战|多传感器采集联动全流程
c语言·stm32·单片机·嵌入式硬件·物联网·1024程序员节
CodeYp201917 小时前
第11讲:stack 和 queue
开发语言·c++·queue·stack
Zwawa18 小时前
一只按键不只“按下”:短按、长按和连按如何共存
c语言·单片机·嵌入式
沫璃染墨18 小时前
《Qt从零入门系列(十五):Qt线程安全与锁机制详解——从QMutex到RAII与读写锁实战》
开发语言·c++·qt·安全·交互·线程·qt6.3
hetao173383718 小时前
2026-10-25 hetao1733837 的刷题记录
c++·算法
fqbqrr19 小时前
2610C++,UI复合技术在窗口中绘画字体图标
c++·ui