Jim Calabro

使用 CGO 和 Zig 构建 Go 静态链接可执行文件

这是一篇简短的文章,介绍如何创建一个静态链接的 Go 可执行文件,该文件使用 Zig 调用 CGO 依赖项。 本文的完整代码可在 此仓库 中找到。

默认情况下,如果您使用 CGO,则生成的可执行文件是动态链接的,但我经常希望进行静态链接以避免运行时错误。

首先,让我们使用 zig init 创建一个 Zig 库,然后删除它生成的额外内容,以便我们只剩下一个简单的静态库。 你可以 rm src/main.zig,因为我们不是要创建一个 Zig 可执行文件。

接下来,我们可以将 build.zig 简化为:

// build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
  const target = b.standardTargetOptions(.{});
  const optimize = b.standardOptimizeOption(.{});
  const lib_mod = b.createModule(.{
    .root_source_file = b.path("src/root.zig"),
    .target = target,
    .optimize = optimize,
  });
  const lib = b.addLibrary(.{
    .linkage = .static,
    .name = "cgo_static_linking",
    .root_module = lib_mod,
  });
  b.installArtifact(lib);
}

我们可以保留 build.zig.zon 文件不动。

现在,让我们在 src/root.zig 中编写一个使用 C ABI 的简单库函数:

// src/root.zig
const std = @import("std");
pub export fn my_zig_function() void {
  std.debug.print("Hello from zig!\n", .{});
}

以及它对应的 C 头文件,命名为 zig_lib.h

// zig_lib.h
#pragma once
void my_zig_function();

Zig 方面就这样! 现在可以通过简单地运行 zig build 来构建库。

让我们编写调用它的 Go 程序。

// main.go
package main
/*
#cgo LDFLAGS: -L./zig-out/lib -lcgo_static_linking -static
#include "zig_lib.h"
*/
import "C"
import "fmt"
func main() {
	fmt.Println("starting program")
	defer fmt.Println("done")
	C.my_zig_function()
}

我们现在将构建 Go 可执行文件,并使用此 Bash 命令将其静态链接:

CC="zig cc -target x86_64-linux-musl" \
CGO_ENABLED=1 \
CGO_LDFLAGS="-static" \
GOOS=linux GOARCH=amd64 \
go build -a -ldflags '-extldflags "-static"' main.go

让我们检查一下我们的工作:

$ ./main
starting program
Hello from zig!
done
$ ldd ./main
    not a dynamic executable

在我看来不错!

我非常感激我正处于工具变得非常优秀的时代。 Go 和 Zig 都很棒!

如果您发现这很有用,不妨考虑向 Zig Software Foundation 捐款!