同步操作将从 deepinwiki/wiki 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
[TOC]
什么是构建系统。 程序源代码需要通过脚本按部就班的编译成程序,这构建程序的过程,或者说是工具,就叫构建系统。你可以不懂开发程序,但是使用 linux 难免要编译和构建软件,因为很多开源免费的软件,都是只是提供源代码,让你自己构建的。
构建系统有很多,比如古老的 autotools 和 make, 然后比较新潮的 cmake 和 ninja 。但是他们都有一个问题,就是——复杂。
有没有一种比较简单的构建系统? 我不知道,但是这个 xmake 是国人开发的,应该比较适合国人的使用习惯。尝试一下。
xmake cli 工具的功能有:
其余杂项:
xmake 使用一个 xmake.lua 作为构建的配置文件,但并非所有配置都需要在配置文件中设置,xmake 通过命令直接配置大部分参数(自动识别大部分设置)。
xmake 基本命令包括:创建项目(create)、构建(build/b)、打包(package/p)、清理(clean/c)、组件依赖(require/q)、运行(run/r)、安装(install/i)、卸载(uninstall/u)、配置(config/f)、全局配置(global/g)、更新(update)。
扩展命令:插件(plugin)、存储库(repo)、回显(echo)、信息(show)、项目转换(project)、宏(macro/m)、分布式编译服务(service)、文档(doxygen)、 转换ipa(app2ipa)、脚本(lua/l)
简要说明:
hello # 工程目录
├── src # 源代码目录
│ └── main.c # 源代码文件
└── xmake.lua # 工程脚本
多级配置:子目录下的 xmake.lua 自动继承上级父目录的 xmake.lua 相关设置(公共部分)。
# 编译器应该遵从这个目录布局
toolchains_sdkdir/ # 支持交叉编译的编译器目录
├── bin
│ ├── arm-linux-armeabi-gcc # 带有平台和架构前缀的工具
│ └── arm-linux-armeabi-ld
├── include
│ └── xxx.h
└── lib
└── libxxx.a
常见编译工具:
xmake.lua 是 xmake 项目的主要配置文件,类似 make 构建系统中的 Makefile。xmake.lua 相对 Makefile 的优势,主要体现在这个配置文件比较好理解。尤其因为这个作者是中国人,思维模式比较符合国人的习惯。
它使用一种叫 lua 的语言编写,Lua 是一种轻量小巧的脚本语言。具体看案例,将来深入也可以学一下 lua。
-- xmake.lua 简单样例
target("test") -- 项目目标,一个项目可以有多个目标,每个目标就是一组构建方案
set_kind("binary") -- 对应 cli 配置中的 --kind
add_files("src/*.c") -- 添加源文件,表示 src 子目录下的所有 c 源文件
-- 也可以将以上改成合并成一行的写法
target("test2",{kind="binary", files="src/*.c"})
区区三行,就是一个完整的配置文件,这就是 xmake 简洁的地方。
xmake 对配置语法做了两种分类:
以上就是个配置域的简单例子,只需要简单设置相关参数的值即可。target 就是一个配置域,set_kind、add_files 就是这个域的配置项,不同的 target 有各自的配置项设置。
配置域类型:
脚本域:本质就是定义在特定时期自动运行的函数。因为设置在特定时期自动调用,所以它有明确的执行流程。
配置域可能被构建系统多次扫描和执行,虽然配置域也能编写简单的脚本流程,但基于以上理由应该尽量避免,尤其是编写复杂的脚本。
xmake 工程可以设定多个子目标(target),也可以有子目录(子项目),但是以上设计对于更加独立的依赖项目来说,是不方便的。这就要使用到包管理方法。
被依赖的包,它实际并非是本项目的子项目,因为它也能是其他工程的依赖项,所以并非是内部关系。
xmake p # 打包 build/xxx.pkg/...
xmake p -o packages # 打包并输出到 packages 子目录
tree packages # 包的目录结构
packages/
└── test2.pkg # 包名.pkg
├── linux # plat
│ └── i386 # arch
│ └── lib # lib,只有库类型才会打包
│ └── release # mode
│ └── libtest2.a # test2 静态库
└── xmake.lua # 包的配置信息
设置包依赖:
add_packagedirs("packages") -- 包的搜索路径
target(test2_demo) -- 测试项目
set_kind("binary")
add_packages("test2") -- 依赖 test2 包
add_files("src/main.c")
包的配置信息:
-- test2.pkg/xmake.lua
option("test2")
set_showmenu(true)
set_category("package") -- 类别
add_links("test2") -- 链接
add_linkdirs("$(plat)/$(arch)/lib/$(mode)") -- 库目录
add_includedirs("$(plat)/$(arch)/include") -- 头文件目录
可见,当添加包依赖的时候,自动添加库目录和头文件,还有链接的库等参数。
包的种类:
# 尝试搜索第三方的 openssl 包
xmake l find_packages openssl
可能得到信息:
{
{
links = {
"crypto"
},
version = "1.1.1h"
}
}
三种包中,本地包是基础的,系统包是为了兼容性考虑,而真正值得注意的是远程包,因为它试图实现一个自动管理的软件仓库。如果我们的依赖项都能自动管理,那么构建软件就是一件相对轻松的事情。
xmake 包管理的逻辑图:
xmake 远程包和本地包的组织方式是完全不同的。
从概念上讲,本地包是二进制包,而远程包是脚本包。脚本下载源码(或二进制包),然后按步骤编译,再安装到系统里面,这就是远程包做的任务。
目录结构:
xmake-repo/
└── packages
├── t
│ └── tbox
│ └── xmake.lua
└── z
└── zlib
└── xmake.lua
可见仓库里面的包并不包含源码或二进制文件。
仓库包的 xmake.lua 只允许 package 配置域:
package("zlib") -- 包名
set_homepage("xxx") -- 官网
set_description("") -- 描述信息
set_kind("") -- 包类型,和本地包不同的是,可以支持可执行程序
set_urls() -- 下载链接
add_urls() -- 不同版本的下载链接/镜像源
add_versions() -- 可选版本和 sha256 散列值
add_patches() -- 补丁
add_links() -- 链接参数
add_configs() -- 提供给外部的配置接口
add_defines() -- 提供给外部的定义
on_load() -- 脚本域,动态更改基本的设置
on_install() -- 编译
on_test() -- 测试
使用仓库的包:
-- test2/xmake.lua
add_requires("test2") -- 依赖项
target("test_demo")
add_packages("test2")
set_kind("binary")
add_files("src/main.c")
运作机制概要:
仓库可以是:
# 添加一个项目内部的仓库
xmake repo --add my-repo myrepo
# 添加一个外部的全局的本地仓库
xrepo add-repo g-repo /home/htqx/grepo
# 也可以是 git 远程仓库
源码包可以是 xmake 项目,也可以是第三方项目。但是 xmake 原生项目会简单很多。不过现在还不支持自动生成源码包,需要自己打包源码。同样的,也不支持从本地包自动转换成远程包。
将普通 xmake 项目源代码打包成远程包规格。
# 这是一个标准模板项目
xmake create -t static -l c test2
tree test2 # 例子项目
test2
├── src
│ ├── interface.c
│ ├── interface.h
│ └── main.c
└── xmake.lua
cd test2
tar -uvf test2-1.0.0.tar xmake.lua src # 打包源码和 xmake.lua
sha256sum test2-1.0.0.tar # 获取源码包的哈希值,用在远程包的配置上
mkdir -pv ../myrepo/packages/t/test2 # 创建仓库目录
nano ../myrepo/packages/t/test2/xmake.lua # 编写远程包脚本
远程包脚本:
-- xmake.lua
package("test2")
set_urls("/test2-$(version).tar") -- 本地填写绝对路径
add_versions("1.0.0","8c9f08f7a47248a9a8a2733583e7016ed8c2c9dafc57eb6f24ff0f3988dea932") -- 版本号,sha256 哈希值,虽然作者说是可选的,但我测试是必要的
on_install(function(package)
import("package.tools.xmake").install(package)
end)
真实项目中,这个应该是个 git 地址,这里用了一个虚假的地址。但路径不存在没关系,可以指定搜索目录:
xmake g --pkg_searchdirs = /home/htqx/Documents/xmake/test2
当下载不了的时候,会在这个目录搜索源码包。
# 用户程序的例子:
cd ..
xmake create -l c test1 # 新创建一个测试项目
cd test1
nano xmake.lua # 修改用户程序的配置
-- xmake.lua
add_rules("mode.debug", "mode.release")
add_repositories("my-repo $(projectdir)/../myrepo") -- 仓库
add_requires("test2 1.0.0",{verify=false}) -- 依赖包,1.0.0 表示版本(可省略),verify 参数表示不验证 sha256 散列值(本人测试没用)
target("test1")
set_kind("binary")
add_packages("test2") -- 导入当前项目目标中,即自动添加链接库等参数
add_files("src/main.c")
我在想既然本地包那么简单就自动生成了,为啥远程包要如此麻烦?能不能从本地包转换到远程包?这个案例手动转换一下:
# 紧跟上个案例,在 test1 同级目录
xmake create -t shared -l c test3
cd test3
xmake p # 构建二进制包,build/test3.pkg/...
cd build
tar -cvf test3-1.0.0.tar test3.pkg # 打包二进制包
sha256sum test3-1.0.0.tar # 获取哈希值
cp test3-1.0.0.tar ../test2 # 拷贝到源码搜索目录下
mkdir -pv ../myrepo/packages/t/test3/ # 在本地仓库创建 test3 目录
nano ../myrepo/packages/t/test3/xmake.lua # 编写远程包脚本
package("test3")
set_urls("/test3.pkg-$(version).tar")
add_versions("1.0.0","1998d892e68ae0a3d8f851c946ab3d637ed6cb030171e893e9958f33e304dfc5")
add_linkdirs("$(plat)/$(arch)/lib/$(mode)") -- 修改库地址
-- add_includedirs("$(plat)/$(arch)/include") -- 经过测试,并不支持修改头文件位置
on_install(function(package)
import("package.tools.xmake").install(package)
os.cp("$(plat)",package:installdir("")) -- 复制二进制包的内容到安装目录
os.cp("$(plat)/$(arch)/include/*",package:installdir("include")) -- 复制二进制包的头文件到默认的头文件文件夹
end)
转换并不完美,主要是头文件位置不能修改。我不能理解为何作者不统一本地包和远程包的安装目录结构,本地包设计能支持交叉编译,而远程包的目录结构只有一层 /lib 和 /include 。
建议作者可以自动生成远程包,因为这本来就是一件比较简单,却又经常要用的,重复性很高的任务。另外,编写脚本过程中,我找不到安装目录路径的关联变量。
使用方法是一样的,这里就不演示了。有用的命令:
xmake q --info test3 # 显示远程包的安装信息
xmake q -l # 列出项目安装的包
xmake q -f # 强制重新安装包,比如更新了包
xmake -v # 构建并查看详细输出
xmake f -v # 查看当前配置结果
xmake f -c # 清除当前临时配置
插件目录:
mkdir -p plugins/hello
nano plugins/hello/xmake.lua # 编写插件
nano xmake.lua # 当前项目配置中添加插件目录 add_plugindirs("./plugins")
-- ./plugins/hello/xmake.lua
task("hello")
set_category("plugin")
on_run(function()
print("hello xmake!")
end)
set_menu{
usage = "xmake hello [option]",
description="hello xmake!",
options={}
}
--👅🇨🇳🔯
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。