遇到一个小众而又趣的开发语言Zig,目前还在开发中,初步实现自举,目前没有发布1.0版本,当前发布的最新版本是0.10.1。

Zig 可以在项目直接加入 C 代码实现混编,Zig 可以作为 C/C++ 语言的编译器。

Zig 不支持宏编程,但支持comptime关键字,在编译时运行代码和惰性求值,编写通用代码或是进行元编程。

Zig 没有像 Rust 那样实现内存安全,可 Zig 也没有 Rust 那么复杂。

Zig 内置测试功能,还自带很完备的命令工具,这点是不是和 Go 很像?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ zig
Usage: zig [command] [options]

Commands:

  build            Build project from build.zig
  init-exe         Initialize a `zig build` application in the cwd
  init-lib         Initialize a `zig build` library in the cwd

  ast-check        Look for simple compile errors in any set of files
  build-exe        Create executable from source or object files
  build-lib        Create library from source or object files
  build-obj        Create object from source or object files
  fmt              Reformat Zig source into canonical form
  run              Create executable and run immediately
  test             Create and run a test build
  translate-c      Convert C code to Zig code

  ar               Use Zig as a drop-in archiver
  cc               Use Zig as a drop-in C compiler
  c++              Use Zig as a drop-in C++ compiler
  dlltool          Use Zig as a drop-in dlltool.exe
  lib              Use Zig as a drop-in lib.exe
  ranlib           Use Zig as a drop-in ranlib
  objcopy          Use Zig as a drop-in objcopy

  env              Print lib path, std path, cache directory, and version
  help             Print this help and exit
  libc             Display native libc paths file or validate one
  targets          List available compilation targets
  version          Print version number and exit
  zen              Print Zen of Zig and exit

General Options:

  -h, --help       Print command-specific usage

Zig

⚡ 一种简单的语言

专注于调试你的应用程序,而不是调试你的编程语言知识

  • 没有隐式控制流
  • 没有隐式内存分配
  • 没有预处理器,没有宏

⚡ 编译期代码执行

基于编译期代码执行和惰性求值的全新元编程方法

  • 编译期调用任意函数
  • 在没有运行时开销的情况下,将类型作为值进行操作
  • 编译期模拟目标架构

⚡ 用Zig维护代码

逐步改善你的C/C++/Zig代码库

  • 将Zig作为一个零依赖的,支持开箱即用交叉编译的C/C++编译器
  • 利用 zig build在所有平台上创建一个一致的开发环境
  • 在C/C++项目中添加一个Zig编译单元,跨语言LTO默认启用

官网文章 深入了解 从系统编程的角度对 Zig 进行深入的功能了解,有了 C++、D 和 Rust,为什么还需要 Zig? 介绍了Zig 同其他语言的区别。

ziglings 项目可以通过修补和调试一系列带有故障的小程序来学习 Zig 。

ziglearn.org 有一份 Zig 在线学习教程。

Hello World

创建hello.zig 文件,内容:

1
2
3
4
5
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, {s}!\n", .{"World"});
}

使用命令zig run hello.zig 来构建和运行:

1
2
$ zig run main.zig
Hello, World!

也可以通过Zig 内置命令来完成,运行:

1
2
3
mkdir hello-world
cd hello-world
zig init-exe 

输出:

1
2
3
info: Created build.zig
info: Created src/main.zig
info: Next, try `zig build --help` or `zig build run`

运行 zig build run 应该会编译成可执行文件并运行,最终结果将会是:

1
2
3
$ zig build run
All your codebase are belong to us.
Run `zig build test` to run the tests.

LSP

官方支持的一个language server 是 zls ,目前主流编辑器都有Zig 的语法高亮支持、自动补全、定义跳转等。

构建

Zig 自带构建相关命令行工具:

  • zig build 通过build.zig 构建项目(build project)
  • zig init-exe 初始化一个zig build 应用程序项目
  • zig init-lib 初始化一个 zig build 类库项目

编译模式

Zig 提供四种编译模式,Debug模式是默认模式,因为它需要的编译时间最短。

运行时安全 优化
Debug Yes No
ReleaseSafe No Yes, Speed
ReleaseSmall No Yes, Size
ReleaseFast No Yes, Speed

这些可以在zig runzig test 命令中,通过使用参数 -O ReleaseSafe, -O ReleaseSmall-O ReleaseFast 来启用。

构建可执行文件

zig build-exe, zig build-lib, zig-obj 分别用于构建输出 可执行文件、lib文件、obj文件,这些命令都接受源文件和参数。

几个常见参数:

  • -fsingle-threaded 断言二进制文件是单线程的, 这会导致如互斥锁之类的线程安全措施变成空操作。
  • -fstrip 从二进制文件中移除调试信息
  • --dynamic它与 zig build-lib 结合使用以输出动态/共享库。

如对上面的hello.zig 执行命令 zig build-exe ./main.zig -O ReleaseSafe -fstrip -fsingle-threaded 生成一个24k可执行的文件,而使用命令zig build-exe ./main.zig -O ReleaseSmall -fstrip -fsingle-threaded生成的可执行文件只用14k。

1
2
3
4
5
6
// hello.zig
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, {s}!\n", .{"World"});
}

交叉编译

默认情况下,Zig 会根据你的 CPU 和操作系统进行编译。这可以通过-target参数进行覆盖,可以通过下面的命令将hello编译到64位arm linux 平台:

1
zig build-exe ./main.zig -O ReleaseSmall -fstrip -fsingle-threaded -target aarch64-linux

可以对一下的CPU架构交叉编译:

  • x86_64
  • arm
  • aarch64
  • i386
  • riscv64
  • wasm32

可以对一下操作系统进行交叉编译:

  • linux
  • macos
  • windows
  • freebsd
  • netbsd
  • dragonfly
  • UEFI

-target 参数格式是: <arch><sub>-<os>-<abi>,通过运行命令zig targets 可以找到可以支持的架构、操作系统、CPU、ABI详细信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$ zig targets | jq "keys"
[
  "abi",
  "arch",
  "cpuFeatures",
  "cpus",
  "glibc",
  "libc",
  "native",
  "os"
]
$  zig targets | jq ".native"
{
  "triple": "x86_64-macos.13.3.1...13.3.1-none",
  "cpu": {
    "arch": "x86_64",
    "name": "skylake",
    "features": [
      "64bit",
      "adx",
      "aes",
      "allow_light_256_bit",
      "avx",
      "avx2",
      "bmi",
      "bmi2",
      "clflushopt",
      "cmov",
      "crc32",
      "cx16",
......

如: 对于M1架构的 MacOS 可以使用aarch64-macos, Intel x86 CPU版 MacOS 使用x86_64-macos-none,x86 64 位CPU Windows 使用x86-windows-gnu,x86 架构64位CPU Linux 使用x86-linux-gnu:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ cat main.zig
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, {s}!\n", .{"World"});
}
$ zig build-exe ./main.zig -O ReleaseSmall -fstrip -fsingle-threaded -target x86_64-macos 
$ ./main
Hello, World!
$ zig build-exe ./main.zig -O ReleaseSmall -fstrip -fsingle-threaded -target x86_64-windows-gnu
$ ls 
main.zig main.exe

Zig 也是 C 编译器

1
2
3
4
5
6
7
$ cat hello.c 
#include <stdio.h>

int main(int argc, char **argv) {
    fprintf(stderr, "Hello, World!\n");
    return 0;
}
1
2
3
$ zig build-exe ./hello.c -O ReleaseSmall -fstrip
$ ./hello
Hello, World!

连 1.0 版本都没有,Uber 为什么会采用这样一项新技术? Uber 使用 Zig 来 编译其 C/C++代码,与其他工具链相比,zig-cc 提供的 C/C++工具链的主要优势是:glibc 版本可配制与 macOS 交叉编译。但 Uber 没有任何使用 zig-the-language 的计划。

参考

  1. make self-hosted the default compiler #12368
  2. How Uber Uses Zig
  3. Bazel C/C++ toolchain for cross-compiling C/C++ programs
  4. Zig Language Reference
  5. ziglearn.org
  6. Mastering project managment in Zig