xmake的简易学习

article/2025/6/8 5:42:49

文章目录

      • 1. xmake是什么
      • 2. 一个可执行程序
      • 3. 一个库文件
      • 4. 遍历文件用法
      • 5. 第三方库
        • 3.1 系统安装库
        • 3.2 独立库
      • 6. 后续

由于前一篇博客的最后说要做一些rknn的优化,其实这个工作很早就完成了,但是我是使用 xmake这个来做我的工程的构建的,不管是出于对之后的博客的铺垫,还是对 xmake的欣赏和喜欢,我觉得有必要写这么一篇博客。

1. xmake是什么

相信我们写C/C++的更多的都知道CMake,也大致知道这个东西在当前跨平台编译的地位。或者还会了解其他的,比如:makefileninjia等等。

但是不知道你有没有像我一样,在写CMakeLists.txt的时候,总是吐槽这凌乱的语法,不关心大小写就算了,然后各种东西又臭又长,尤其是写面向对象多了以后,就更讨厌CMake这丑陋的语法。尤其是看到gopython这些语言中,特别好用方便的包管理的时候,就更嫌弃了。

我也是偶然间看到了xmake的,有中文的文档,简洁的语法(如果你有lua语言的基础,就更喜欢这样的语法),还有一个特别方便的包管理,我是就自个儿看着文档学了起来。

后来我自己的github上一些小代码也都使用xmake进行构建,还有自己本地的gitlab的代码也都使用xmake了,可能写的水平不是很高,但带来的方便是让我很受用的。

OK,废话说完,真心希望大家可以看看xmake,也许你也会喜欢上用这个工具。

2. 一个可执行程序

由于xmake官方的文档已经非常详细了,而且还有中文的文档,非常适合我们自学和使用,我就不卖弄知识,就简单根据自己的使用,写几个很简单的demo来介绍一下xmake的基础用法。

首先就是一个可执行程序,我还是用最经典的HELLO WORLD来说。

有这样一个目录结构:

- src
-- main.cpp
- xmake.lua

就这样的一个简单的示例,注意到这里有一个xmake.lua,这个就是我们进行构建的核心。那么我们这个xmake.lua怎么写?我直接给一个很简单的demo

-- 设置项目名称,可有可无
set_project("xmake_exec")
-- 设置支持的编译模式
add_rules("mode.debug", "mode.release")-- 配置 debug 下的一些编译选项
if is_mode("debug") then    -- 启用 debug 时的调试符号set_symbols("debug")-- 禁用优化set_optimize("none")
end-- 配置 release 下的一些编译选项
if is_mode("release") then-- 隐藏调试符号set_symbols("hidden")-- 设置优化set_optimize("fastest")-- 删除所有的调试符号set_strip("all")
end-- 将所有的警告都视为错误
set_warnings("all", "error")-- 设置 c99 c++11
set_languages("c99", "c++11") -- cxx11-- 添加一些宏定义
-- add_defines("NDEBUG", "_GNU_SOURCE=1")-- 设置包含目录
-- add_includedirs("/usr/include", "/usr/local/include")-- 设置依赖的库和目录
-- add_links("tbox")
-- add_linkdirs("/usr/local/lib", "/usr/lib")-- 添加系统链接库
-- add_syslinks("z", "pthread")-- 添加编译链接的参数
-- add_cxflags("-stdnolib", "-fno-strict-aliasing")
-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true})target("xmake_exec")set_kind("binary")add_files("src/*.cpp")

这里面写了很多注释的东西,都是一些暂时没用上的,可以供后续使用的时候添加。这里的各种“函数”,其实都可以在xmake官方文档中有详细的介绍和demo,我肯定没有作者写的深入和详细,所以如果想要了解每一个“函数”的作用,还请认真看一下。

我这里直接说target这个部分。

顾名思义,target就是目标,也就是我们当前这个xmake.lua所要构建的目标,只需要上述那么简单的三行就可以搞定,我们可以对应的理解CMake中的:

add_executable(xmake_exec src/main.cpp)

当然,如果你的文件特别多的时候,你就只能使用CMakefile(GLOB ...)来实现了,但是xmake就是上述简单的add_files("src/*.cpp)来实现,是不是很优雅,尤其是你在Linux下使用shell命令习惯了之后的写法?

题外话,我觉得xmake使用的特别舒服的一点就是,它能让你觉得“就应该是这样的嘛”,而不是CMake那样很繁琐且难用的方式。

3. 一个库文件

如果只是写一个可执行程序,那也太没水平了,只能用来做做测试,那么如何生成一个库文件?

在这里插入图片描述

OK,这样一个目录结构,有头文件,有源文件,然后还有我们的xmake.lua,那我们的xmake.lua也很简单:

-- 设置项目名称
set_project("xmake_lib")-- 设置版本
set_version("1.0.0")-- 设置xmake的最低要求版本
set_xmakever("2.6.9")-- 设置支持的编译模式
add_rules("mode.debug", "mode.release")-- 配置 debug 下的一些编译选项
if is_mode("debug") then    -- 启用 debug 时的调试符号set_symbols("debug")-- 禁用优化set_optimize("none")
end-- 配置 release 下的一些编译选项
if is_mode("release") then-- 隐藏调试符号set_symbols("hidden")-- 设置优化set_optimize("fastest")-- 删除所有的调试符号set_strip("all")
end-- 将所有的警告都视为错误
set_warnings("all", "error")-- 设置 c99 c++11
set_languages("c99", "c++11") -- cxx11-- 添加一些宏定义
add_defines("DREAMSKY_EXPORTS")-- 设置包含目录
-- add_includedirs("/usr/include", "/usr/local/include")-- 设置依赖的库和目录
-- add_links("tbox")
-- add_linkdirs("/usr/local/lib", "/usr/lib")-- 添加系统链接库
-- add_syslinks("z", "pthread")-- 添加编译链接的参数
-- add_cxflags("-stdnolib", "-fno-strict-aliasing")
-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true})target("xmake_lib")    -- 这样可以在外部直接传参,从而构建需要的set_kind("$(kind)")-- set_kind("shared")-- set_kind("static")-- 如果需要外部配置是否需要进行传参,比如 --var=val 的时候,可以使用-- add_defines("-DTEST=$(var)")add_includedirs("include", {public = true})-- 如果头文件的目录比较复杂,那么就需要这样进行处理-- 通配符include/**.h匹配include目录及其子目录的所有.h后缀文件。-- 对于add_headerfiles语句,如果不加括号,则所有文件都会被直接安装到include文件夹下,-- 目录结构将会丢失;而括号的作用在于保持括号内的目录结构。-- 例如a/(b/c.h)安装后会变成include/b/c.h。-- 而在设置中的prefixdir选项则将所有头文件放在include的子目录中。-- 如对于上述设置{prefixdir = "mylib"},a/(b/c.h)安装后会变成include/mylib/b/c.h-- add_headerfiles("include/(**.h)", {prefixdir = "DreamSky"})add_headerfiles("include/*.h")add_files("src/*.cpp")-- 有时候使用xmake构建的库需要导出给使用其他构建系统的项目使用,-- 这就需要对应构建工具的配置文件。xmake提供pkg-config配置文件和cmake配置文件的生成。-- 对于需要导出的target,使用如下语句:add_rules("utils.install.pkgconfig_importfiles")add_rules("utils.install.cmake_importfiles")-- 对于头文件之外的安装文件,xmake提供了类似的接口add_installfiles,-- 它与add_headerfiles的区别在于,prefixdir将直接放在安装目录下而不是include文件夹下。-- 例如文档安装可以写-- add_installfiles("doc/*.md", {prefixdir = "share/doc"})if is_plat("windows") and is_kind("shared") thenprint("windows shared")end-- 有时候,项目生成的库和二进制不要按约定的bin和lib目录存放,甚至不需要被安装。-- 还有时候,安装的文件需要根据安装目录做一定的更改。-- 这时可以使用on_install语句来重载target的安装过程。例如,将生成的库文件安装到xmake_lib文件夹:-- on_install(function (target)--     local libdir = path.join(target:installdir(), "xmake_lib")--     os.mkdir(libdir)--     os.cp(target:targetfile(), libdir)--     local includedir = path.join(target:installdir(), "xmake_lib_include")--     os.mkdir(includedir)--     for _, headerfile in ipairs(target:headerfiles()) do--         os.cp(headerfile, includedir)--     end-- end)-- 编译安装命令示例:
-- 清除编译配置: xmake clean
-- 清除所有:     xmake clean --all
-- 指定编译器:   xmake f -p mingw       xmake f -p windows
-- 指定编译模式: xmake f -m debug       xmake f -m release
-- 指定库类型:   xmake f -k shared      xmake f -k static
-- 指定安装目录:  xmake install -o "D:/install/xmake_lib"-- 简短示例:
-- xmake f -p mingw -m debug -k shared
-- xmake
-- xmake install -o "D:/install/xmake_lib"

我也是加了一些注释,我觉得似乎都不需要额外的说明了,就很清晰了。

当然新版本的xmake已经支持给so文件加上版本号了,也就是很简单的:

set_version("1.0.0", {soname = true})

4. 遍历文件用法

很多时候我们需要写很小的demo来验证某个函数,或者某个小算法,好吧,总不能每个文件写一个xmake工程吧?不仅要新建工程,还得写xmake.lua,就很麻烦。

能不能我随意添加文件,然后又能够根据我的文件名自动生成对应的target,然后每次只需要在工程中新建一个.cpp文件就可以了呢?

当然可以!

假设,有这样的目录:

在这里插入图片描述

以后我要是再随便增加test_04.cpp,不用改xmake.lua可不可以做到?答案是肯定的。

那这里的xmake.lua就可以这样写:

-- 遍历和生成部分参考:
-- https://github.com/idealvin/coost/blob/master/test/xmake.lua-- 设置项目名称
set_project("xmake_foreach")-- 设置版本
set_version("1.0.0")-- 设置xmake的最低要求版本
set_xmakever("2.6.9")-- 设置支持的编译模式
add_rules("mode.debug", "mode.release")-- 配置 debug 下的一些编译选项
if is_mode("debug") then    -- 启用 debug 时的调试符号set_symbols("debug")-- 禁用优化set_optimize("none")
end-- 配置 release 下的一些编译选项
if is_mode("release") then-- 隐藏调试符号set_symbols("hidden")-- 设置优化set_optimize("fastest")-- 删除所有的调试符号set_strip("all")
end-- 将所有的警告都视为错误
set_warnings("all", "error")-- 设置 c99 c++11
set_languages("c99", "c++11") -- cxx11-- 使用函数来遍历cpp文件夹,后续根据遍历的结果来处理
function all_tests()local res = {}for _, x in ipairs(os.files("**.cpp")) dolocal item = {}local s = path.filename(x)table.insert(item, s:sub(1, #s - 4))       -- 取文件名来作为target的名字table.insert(item, path.relative(x, "."))  -- 利用path.relative来转换相对路径,即将 x 转换为相对于 . 的相对路径table.insert(res, item)endreturn res
endfor _, test in ipairs(all_tests()) do
target(test[1])set_kind("binary")-- set_default(false)add_files(test[2])
end

这是直接参考xmake作者的coost来进行实现的,额外说一句,作者的coost库也很强,而且没有Boost那么庞大复杂,如果想学习一些编程的思路,可以阅读这个源码,还是很容易看明白的。

5. 第三方库

当然xmake有一个特别好用的仓库,你添加opencv这些依赖的时候,只需要:

add_requires("opencv")target("test")add_files("src/*.cpp")add_packages("opencv")

就可以了,而且可以自动从网络上下载相关的库文件。

3.1 系统安装库

当然,如果你系统上已经安装了这个opencv库,你就是不想用xmake repo的库,也是可以的。

add_requires("cmake::OpenCV", {alias = "opencv", system = true})target("test")add_files("src/*.cpp")add_packages("opencv")

是不是很简单,可以调用cmake的库,当然xmake支持的可多了,具体的参考官方文档。

3.2 独立库

这些都是安装的,那么对于我们使用rknn来说,他就是提供了头文件和库文件,我们怎么来加入到编译呢?当然你可以直接将头文件和库文件加到target的编译中来,可是那也太不优雅了,而且工程多了起来就乱。所以我们需要一个优雅的使用方法,有没有呢?有!

我们可以通过创建一个本地的xmake repo来实现,将他们按照xmake的语法来进行描述,然后就可以在我们的工程中直接使用了,下一篇写rknn的优化的时候将详细说如何将rknnrtrga来优雅地写到我们的xmake repo中。

假设我们已经能将rknnrt按照xmake语法写好了,并且我们的xmake repo地址是/home/xxx/xmake_repo,那么我们工程中直接就可以:

add_repositories("local-repo /home/xxx/xmake_repo")
add_requires("rknnrt", "librga")target("rknn_engine")add_includedirs("include", {public = true})add_headerfiles("include/(**.h)")add_files("src/**.cpp")add_packages("rknnrt", "librga")

是不是很优雅了?

6. 后续

xmake的官方文档已经足够详细了,我觉得没有那个水平能超过作者的文档,只是在自己的使用过程中有一些心得体会,也在自己的githubgitlab和工作中使用了xmake,水平也不算高,如果你也恰好是入门,有一些我也恰好知道的问题,欢迎一起交流。


http://www.hkcw.cn/article/GgREpMfhId.shtml

相关文章

【网络安全 | 信息收集】灯塔(资产收集工具)安装教程

文章目录 简介安装教程1.创建文件2.执行命令3.运行程序简介 ARL(Asset Reconnaissance Lighthouse)资产侦察灯塔系统,旨在快速侦察与目标关联的互联网资产,构建基础资产信息库。 协助甲方安全团队或者渗透测试人员有效侦察和检索资产,发现存在的薄弱点和攻击面。 其特性如…

TCP小结

1. 核心特性 面向连接:通过三次握手建立连接,四次挥手终止连接,确保通信双方状态同步。 TCP连接建立的3次握手 抓包: client发出连接请求; server回应client请求,并且同步发送syn连接; clien…

Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析

附件下载 联系工作人员获取附件 该系列文章将讨论智能手机镜头模组设计的挑战,从概念、设计到制造和结构变形的分析。本文是四部分系列的第三部分,它涵盖了使用 Ansys Zemax OpticStudio Enterprise 版本提供的 STAR 技术对智能手机镜头进行自动的结构…

【Redis】set 类型

set 一. set 类型介绍二. set 命令sadd、smembers、sismemberscard、spop、srandmembersmove、srem集合间操作交集:sinter、sinterstore并集:sunion、sunionstore差集:sdiff、sdiffstore 三. set 命令小结四. set 内部编码方式五. set 使用场…

006网上订餐系统技术解析:打造高效便捷的餐饮服务平台

网上订餐系统技术解析:打造高效便捷的餐饮服务平台 在数字化生活方式普及的当下,网上订餐系统成为连接餐饮商家与消费者的重要桥梁。该系统以菜品分类、订单管理等模块为核心,通过前台展示与后台录入的分工协作,为管理员和会员提…

Python趣学篇:Pygame重现经典打砖块游戏

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《Python星球日记》 目录 一、游戏背景与技术选型1. 打砖块游戏…

Transformer学习资料

​​核心论文​​ 原论文标题:《Attention Is All You Need》(Transformer原始论文) ​​Transformer学习资源​​ 视频教程: B站中文视频:Transformer详解 中文教程: GitHub项目:learn-nlp-wi…

AIGC 基础篇 高等数学篇 02导数与微分

声明:本文章仅用于复习,请不要将本文章当做预习篇或者讲解篇 此外,此文章不会包含全部的高等数学知识,仅仅是为了学习AI而进行的前期学习,因此知识含量不会很多,另外补充一句,博主已经对上一篇…

MQTTX连接阿里云的物联网配置

本文的目标是通过MQTTX的客户端,连接到阿里云的物联网的平台,发送温度信息,在阿里云的平台中显示出来。阿里云免费注册,免费有一个MQTT的服务器。有数量限制,但是对于测试来讲,已经足够。 1、注册阿里云的物…

06-排序

排序 1. 排序的概念及其应用 1.1 排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键…

MS1023/MS1224——10MHz 到 80MHz、10:1 LVDS 并串转换器(串化器)/串并转换器(解串器)

产品简述 MS1023 串化器和 MS1224 解串器是一对 10bit 并串 / 串并转 换芯片,用于在 LVDS 差分底板上传输和接收 10MHz 至 80MHz 的并行字速率的串行数据。起始 / 停止位加载后,转换为负载编 码输出,串行数据速率介于 120Mbps…

Cyber Weekly #58

赛博新闻 1、DeepSeek新版R1更新,幻觉率大幅降低 5月28日,DeepSeek-R1模型已升级至DeepSeek-R1-0528版本,核心在于显著提升模型的思维深度与推理能力。该版本基于DeepSeek V3 Base模型,通过强化后训练显著优化了在数学、编程及通…

换一条宽带ip地址会变吗?同一个宽带如何不同ip地址

宽带IP地址是否变化取决于更换的方式,以及你使用的是公网IP还是内网IP。以下是具体分析,并附上同一个宽带下切换IP的实用方法: 🌐 一、更换宽带是否会改变IP地址? 1. 更换宽带线路(如从电信换到移动&#x…

环境对象以及回调函数

1.环境对象 2.回调函数

SQL Indexes(索引)

目录 Indexes Using Clustered Indexes Using Nonclustered Indexes Declaring Indexes Using Indexes Finding Rows Without Indexes Finding Rows in a Heap with a Nonclustered Index Finding Rows in a Clustered Index Finding Rows in a Clustered Index with …

graphviz, dot, Error: lost rA sA edge; 独立的模块

1) 有向图dot文件 digraph R { node [shaperecord]; { ranksame rA sA tA } { ranksame uB vB wB } rA -> sA; sA -> vB; t -> rA; uB -> vB; wB -> u; wB -> tA; } 2)出现报警信息 Warning: flat edge between adjacent …

SpringBoot接入Kimi实践记录轻松上手

kimi简单使用 什么是Kimi API 官网:https://platform.moonshot.cn/ Kimi API 并不是一个我所熟知的广泛通用的术语。我的推测是,你可能想问的是关于 API 的一些基础知识。API(Application Programming Interface,应用程序编程接…

Windows版PostgreSQL 安装 vector 扩展

问题 spring-ai在集成PGVector向量存储的时候会报错如下,那么就需要安装pgsql的vector扩展。 SQL [CREATE EXTENSION IF NOT EXISTS vector]; 错误: 无法打开扩展控制文件 "C:/Program Files/PostgreSQL/9.6/share/extension/vector.control": No such …

【操作系统原理08】文件管理

文章目录 零.大纲一.文件管理0.大纲1.文件管理1.1 **文件属性**1.2 文件内部数据组织1.3 文件之间的组织1.4操作系统提供功能1.5 文件在外存存放 二.文件的逻辑结构0.大纲1.无结构文件2.有结构文件 三.文件目录0.大纲1.文件控制块2.目录结构3.索引节点(FCB改进) 四.文件共享0.大…

力扣面试150题--二叉搜索树中第k小的元素

Day 58 题目描述 思路 直接采取中序遍历,不过我们将k参与到中序遍历中,遍历到第k个元素就结束 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* …