可移植性问题
打开普通的 Makefile,你会发现大量的能力校验(brittle capability checks)会完全破坏任何形式的交叉编译尝试。实际上,在上一篇文章中,我们在研究 Redis 时已经看到了一个非常温和的版本,但情况可能会更糟。
值得庆幸的是,Zig 的编译器中集成了一个编译系统,可以通过 zig build 子命令来实现。
为什么使用 Zig Build
无依赖性
由于您已经使用了 Zig 编译器,因此您可以在所有支持的平台上构建您的项目,而无需依赖任何系统依赖性,甚至无需依赖构建必备项、Xcode 或 MSVC。
作为一等公民的交叉编译
Zig 将能够从任何目标编译到任何目标视为其主要目标之一。使用 Zig build 可以更容易地集成 Zig 的交叉编译功能。
声明式但无特殊语法
zig 联编子命令依赖于 build.zig 文件。联编系统使用声明式系统来描述联编流水线(对于联编运行程序的检测非常有用),但它使用普通的 Zig 语法。如果你懂 C 或 C++,就能轻松读懂 Zig 代码。
翻译 Makefile
在我们继续之前,我必须提醒您:如果您不熟悉 C/C++ 生态系统,对某些构建系统进行逆向工程并非易事。
例如,如果你想看看我和安德鲁如何将 Redis 的构建系统翻译成 Zig,请查看安德鲁 Vimeo 上的直播流。在视频中,你会发现安德鲁知道该怎么做,因为他在 C/C++ 领域工作了足够长的时间,知道所有的技巧。
了解 build.zig
要进一步了解 Zig 构建系统使用的类型和约定,请参阅 @xq 的系列文章:zig build explained - part 1
完整的 build.zig
这是一个完整的 build.zig 文件,可以将 redis-cli 和 redis-server 与它们各自的静态依赖关系一起打包。这里看不到的是 jemalloc 和 systemd 支持,留给读者自己练习。
显示完整的 build.zig 文件
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
const hiredis = b.addStaticLibrary("hiredis", null);
hiredis.setTarget(target);
hiredis.setBuildMode(mode);
hiredis.linkLibC();
hiredis.force_pic = true;
hiredis.addCSourceFiles(&.{
"deps/hiredis/alloc.c",
"deps/hiredis/async.c",
"deps/hiredis/hiredis.c",
"deps/hiredis/net.c",
"deps/hiredis/read.c",
"deps/hiredis/sds.c",
"deps/hiredis/sockcompat.c",
}, &.{
"-Wall",
"-W",
"-Wstrict-prototypes",
"-Wwrite-strings",
"-Wno-missing-field-initializers",
});
const lua = b.addStaticLibrary("lua", null);
lua.setTarget(target);
lua.setBuildMode(mode);
lua.linkLibC();
lua.force_pic = true;
lua.addCSourceFiles(&.{
"deps/lua/src/fpconv.c",
"deps/lua/src/lapi.c",
"deps/lua/src/lauxlib.c",
"deps/lua/src/lbaselib.c",
"deps/lua/src/lcode.c",
"deps/lua/src/ldblib.c",
"deps/lua/src/ldebug.c",
"deps/lua/src/ldo.c",
"deps/lua/src/ldump.c",
"deps/lua/src/lfunc.c",
"deps/lua/src/lgc.c",
"deps/lua/src/linit.c",
"deps/lua/src/liolib.c",
"deps/lua/src/llex.c",
"deps/lua/src/lmathlib.c",
"deps/lua/src/lmem.c",
"deps/lua/src/loadlib.c",
"deps/lua/src/lobject.c",
"deps/lua/src/lopcodes.c",
"deps/lua/src/loslib.c",
"deps/lua/src/lparser.c",
"deps/lua/src/lstate.c",
"deps/lua/src/lstring.c",
"deps/lua/src/lstrlib.c",
"deps/lua/src/ltable.c",
"deps/lua/src/ltablib.c",
"deps/lua/src/ltm.c",
"deps/lua/src/lua_bit.c",
"deps/lua/src/lua_cjson.c",
"deps/lua/src/lua_cmsgpack.c",
"deps/lua/src/lua_struct.c",
"deps/lua/src/lundump.c",
"deps/lua/src/lvm.c",
"deps/lua/src/lzio.c",
"deps/lua/src/strbuf.c",
}, &.{
"-std=c99",
"-Wall",
"-DLUA_ANSI",
"-DENABLE_CJSON_GLOBAL",
"-DLUA_USE_MKSTEMP",
});
const redis_cli = b.addExecutable("redis-cli", null);
redis_cli.setTarget(target);
redis_cli.setBuildMode(mode);
redis_cli.install();
redis_cli.linkLibC();
redis_cli.linkLibrary(hiredis);
redis_cli.addIncludeDir("deps/hiredis");
redis_cli.addIncludeDir("deps/linenoise");
redis_cli.addIncludeDir("deps/lua/src");
redis_cli.addCSourceFiles(&.{
"src/adlist.c",
"src/ae.c",
"src/anet.c",
"src/cli_common.c",
"src/crc16.c",
"src/crc64.c",
"src/crcspeed.c",
"src/dict.c",
"src/monotonic.c",
"src/mt19937-64.c",
"src/redis-cli.c",
"src/release.c",
"src/redisassert.c",
"src/siphash.c",
"src/zmalloc.c",
"deps/linenoise/linenoise.c",
}, &.{
"-std=c11",
"-pedantic",
"-Wall",
"-W",
"-Wno-missing-field-initializers",
});
const redis_server = b.addExecutable("redis-server", null);
redis_server.setTarget(target);
redis_server.setBuildMode(mode);
redis_server.install();
redis_server.linkLibC();
redis_server.linkLibrary(hiredis);
redis_server.linkLibrary(lua);
redis_server.addIncludeDir("deps/hiredis");
redis_server.addIncludeDir("deps/lua/src");
redis_server.addCSourceFiles(&.{
"src/acl.c",
"src/adlist.c",
"src/ae.c",
"src/anet.c",
"src/aof.c",
"src/bio.c",
"src/bitops.c",
"src/blocked.c",
"src/childinfo.c",
"src/cluster.c",
"src/config.c",
"src/connection.c",
"src/crc16.c",
"src/crc64.c",
"src/crcspeed.c",
"src/db.c",
"src/debug.c",
"src/defrag.c",
"src/dict.c",
"src/endianconv.c",
"src/evict.c",
"src/expire.c",
"src/geo.c",
"src/geohash.c",
"src/geohash_helper.c",
"src/gopher.c",
"src/hyperloglog.c",
"src/intset.c",
"src/latency.c",
"src/lazyfree.c",
"src/listpack.c",
"src/localtime.c",
"src/lolwut.c",
"src/lolwut5.c",
"src/lolwut6.c",
"src/lzf_c.c",
"src/lzf_d.c",
"src/memtest.c",
"src/module.c",
"src/monotonic.c",
"src/mt19937-64.c",
"src/multi.c",
"src/networking.c",
"src/notify.c",
"src/object.c",
"src/pqsort.c",
"src/pubsub.c",
"src/quicklist.c",
"src/rand.c",
"src/rax.c",
"src/rdb.c",
"src/redis-check-aof.c",
"src/redis-check-rdb.c",
"src/release.c",
"src/replication.c",
"src/rio.c",
"src/scripting.c",
"src/sds.c",
"src/sentinel.c",
"src/server.c",
"src/setcpuaffinity.c",
"src/setproctitle.c",
"src/sha1.c",
"src/sha256.c",
"src/siphash.c",
"src/slowlog.c",
"src/sort.c",
"src/sparkline.c",
"src/syncio.c",
"src/t_hash.c",
"src/t_list.c",
"src/t_set.c",
"src/t_stream.c",
"src/t_string.c",
"src/t_zset.c",
"src/timeout.c",
"src/tls.c",
"src/tracking.c",
"src/util.c",
"src/ziplist.c",
"src/zipmap.c",
"src/zmalloc.c",
}, &.{
"-std=c11",
"-pedantic",
"-Wall",
"-W",
"-Wno-missing-field-initializers",
"-fno-sanitize=undefined",
});
}
您也可以在 GitHub 上找到这段代码。
下一步是什么?
既然我们使用了 zig 编译,就可以非常轻松地进行交叉编译,而不需要外部依赖。这是个不错的地方,但如果你还想体验最后的乐趣,我可以教你如何将 Zig 代码添加到现有的 C 项目中。
可重复性脚注
Zig 0.8.1,Redis commit be6ce8a。