标签 Darwin 下的文章

揭秘Go语言中的rune:一段跨越30年的Plan 9往事与UTF-8的诞生传奇

本文永久链接 – https://tonybai.com/2025/05/16/how-rune-came

大家好,我是Tony Bai。

作为 Gopher,我们每天都在和 rune 打交道。在 Go 语言中,它通常被解释为“一个 Unicode 码点”,官方文档也说引入这个术语是为了“简洁”。但你是否曾好奇,这个略带神秘色彩的词汇,究竟源自何方?仅仅是为了简洁吗?

最近,Connor Taffe的一篇精彩博文以及 Go语言之父 Rob Pike 的亲自确认,为我们揭开了一段跨越三十余年,从 Plan 9 操作系统到 UTF-8 编码诞生,再到 Go 语言的历史传奇。今天,就让我们一起,深入 rune 背后的故事。

一句“简洁”,一段 Plan 9 往事

Connor文章中引用的Adam Pritchard的关于限制字符串长度的文章中提到:“请注意,在 Go 中,Unicode 码点通常被称为‘rune’。(Go 似乎是为了简洁而引入了这个术语。)” 而 Go 官方博客《Strings, bytes, runes, and characters in Go》也说:“‘Code point’有点拗口,所以 Go 引入了一个更短的术语:rune。”

Rob Pike 在 Bluesky 上的发言截图

然而,真相远不止于此。Rob Pike 最近在 Bluesky 上澄清(如上图),rune 这个词实际上是 Ken Thompson 在一次为 Plan 9 寻找一个不同于 char(用于字节)的类型名称的头脑风暴中“得意地”提出的,Rob Pike 当即表示赞同。更关键的是,Rob Pike 随后确认,这个命名发生在 Plan 9 为 UTF 和 ISO 10646 寻找类型名称的时期,具体是1991 年 12 月 8 日的晚上!远早于 Unicode 和 UTF-8 的广泛应用,也比 Go 语言的诞生早了数十年。

是的,你没看错,rune 的故事,始于 Plan 9,那个由贝尔实验室传奇人物们(包括 Rob Pike, Ken Thompson 等)创造的操作系统。Go 语言深受 Plan 9 的影响,从链接器架构、并发原语 channel、标识符大小写的可见性规则,到对简洁性的极致追求,都带着浓厚的 Plan 9 印记。rune 便是这血脉传承中的一环。

餐巾纸上的革命:UTF-8 的诞生传奇

要理解 rune 在 Plan 9 中的意义,就不得不提 UTF-8 的诞生。Connor 的文章中引用了一封 Rob Pike 在 2003 年的邮件,详细披露了这段鲜为人知的历史,纠正了“IBM 设计 UTF-8,Plan 9 实现它”的说法。

故事发生在 1992 年 9 月左右的一个晚上,新泽西一家小餐馆的餐巾纸上:

  • 缘起: Plan 9 当时使用 ISO 10646 最初的 UTF(一种16位字符编码)来支持宽字符,但团队对它非常不满。Rob Pike 形容道:“UTF 太糟糕了。它有模192的算术,而且在没有除法硬件的老 SPARC 机器上几乎不可能高效实现。像【/*】这样的字符串可能出现在西里尔字符中间,导致你的俄文文本变成一个 C 语言注释。还有更多问题。它作为一种编码根本不实用。”
  • 契机: 一天下午,X/Open 委员会的一些人(据 Rob Pike 回忆可能来自 IBM 奥斯汀)打来电话,希望 Ken 和 Rob 审查他们的 FSS-UTF (File System Safe UTF) 设计。Ken 和 Rob 意识到这是一个用他们的经验设计一个真正优秀的标准,并让 X/Open 将其推广出去的机会。
  • 餐巾纸上的灵感: 他们接受了挑战,条件是必须快速完成。于是,在那个决定性的晚餐上,Ken Thompson 在餐巾纸上构想出了 UTF-8 的位打包方案。
  • 闪电般的实现: 晚餐后回到实验室,他们便向 X/Open 解释了新方案,并承诺在周一前(据信是 X/Open 的重要投票日)拿出一个完整的运行系统。当晚,Ken 写了打包和解包代码,Rob Pike 则开始修改 C 库和图形库。到周五的某个时候,Plan 9 已经完全运行在后来被称为 UTF-8 的编码上了。

Rob Pike 在邮件中强调,他们之所以要“另起炉灶”,是因为 FSS-UTF 缺少他们认为至关重要的特性之一:支持定位到文件或流的中间,并读取有效字符,或处理损坏的字符。 Ken Thompson 设计的 UTF-8 完美地解决了这个问题。

对比 Ken Thompson 当时提出的 UTF-8 方案(如下图)和 FSS-UTF,我们可以看到 UTF-8 的精妙之处:后续字节以 10 开头,与首字节的 110、1110 等模式区分开来,确保了自同步性和对 ASCII 的兼容性。

Rune 的首次亮相与演变

那么,Rune 这个词是什么时候正式与这种新的字符表示方式联系起来的呢?Rob Pike 在其关于 Plan 9 UTF-8 实现的论文《Hello World》中写道:

“在语义层面上,ANSI C 允许(但并未限制)宽字符的概念,并且允许此类字符串和字符常量。我们选择 unsigned short 作为宽字符类型。在库中,Rune 一词由 typedef 定义为等同于 unsigned short,并用于表示 一个Unicode 字符。”

这似乎是 Rune 作为一种特定类型名称,用于指代 Unicode 字符(码点)的最早文献记录。最初在 Plan 9 C 中,Rune 是一个 16 位无符号短整型,足以表示当时的 Unicode 基本多文种平面(BMP)。

而到了 Go 语言,rune 被定义为 int32 的别名。这是因为自 1992 年以来,Unicode 已经扩展,需要更大的空间来表示所有码点(UCS-4 定义了 31 位码空间)。Go 语言标准库中的 unicode/utf8 包也定义了 UTFMax = 4,表明一个 rune 最多可以用 4 个字节的 UTF-8 编码表示。有趣的是,在 Russ Cox 移植的 plan9 port 中,Rune 类型在 2009 年末也被修改为了 unsigned int,同样是为了支持更广的码点范围。

Ken Thompson 在最初的邮件中提到:“4、5 和 6 字节序列只是出于政治原因才存在的。我更愿意删除它们。” 这也印证了早期设计者对编码效率和实用性的极致追求。

Rune 的足迹:从 Plan 9 到更广阔的世界

Rune 这个术语,并没有止步于 Plan 9。通过 Paul Borman 的贡献,Plan 9 的 rune 功能被整合进了 4.4 BSD。从此,rune 开始在更广阔的 Unix 世界留下足迹:

  • FreeBSD 继承了 4.4 BSD 的 rune 函数,尽管后来推荐使用 ISO C99 的宽字符工具。
  • Apple 的 Darwin 内核,作为 BSD 的衍生,也包含了 rune_t 类型。
  • C 标准库实现如 newlib 也包含了源自 BSD 4.4 的 rune 功能。
  • Android 通过 plan9port 移植了 Plan 9 的 libutf,其中自然也包含了 rune。
  • 甚至,微软的 .NET 在引入 System.Text.Rune 类型时,其灵感也明确来自 Go 语言,这在其 GitHub issue 中由 Miguel de Icaza 提及

可见,rune 这个由 Ken Thompson 灵光一闪提出的词汇,承载着一段从贝尔实验室 Plan 9 开始,经由 BSD 社区,最终深刻影响了包括 Go 在内的现代编程语言和操作系统的字符处理历史。

小结:rune 不只是简洁

通过Rob Pike的亲自确认,我们应该知道,当我们今天再看到 Go 语言中的 rune 时,它不仅仅是为了“简洁”而对“Unicode code point”的替换。它是一个承载着厚重历史的符号,是 Go 语言设计者们深厚技术底蕴和创新精神的体现,是 Plan 9 简洁哲学与 UTF-8 实用主义的结晶。

理解 rune 的来龙去脉,有助于我们更深刻地体会 Go 语言在文本处理、字符串操作以及 Unicode 支持方面的设计考量,也让我们对这门语言背后的巨匠们多一份敬意。下一次,当你在 Go 代码中写下 rune 时,或许会想起那个在新泽西餐馆餐巾纸上诞生的传奇,以及那段跨越三十余年的 Plan 9 往事。

参考文献


聊一聊:

  • 在了解了 rune 的历史后,你对 Go 语言的设计是否有新的认识?
  • UTF-8 诞生的故事中,有哪些细节让你印象深刻?
  • 你认为这种对历史渊源的挖掘,对我们理解和使用一门编程语言有何帮助?

欢迎在评论区分享你的看法!如果你觉得这篇文章有趣且有价值,也请转发给你身边的 Gopher 朋友们,让更多人了解 rune 背后的故事。


今天我们一起挖掘了 rune 这个小小术语背后波澜壮阔的历史,感受到了 Go 语言与 Plan 9、UTF-8 的深厚渊源。真正理解一门语言,往往需要我们深入其“根源”,探究其设计选择背后的“为什么”。

这里,我邀请你加入我在极客时间的专栏 “TonyBai · Go 语言进阶课”

在这门课程中,我们将一起:

  • 夯实基础,突破语法认知瓶颈: 深入剖析那些看似熟悉却暗藏玄机的核心概念。
  • 设计先行,奠定高质量代码基础: 学习如何进行合理的程序骨架、并发设计、包设计、接口设计以及API设计。
  • 工程实践,锻造生产级 Go 服务: 掌握构建可观测性、性能调优、故障排查等硬核技能。

理解“过去”是为了更好地走向“未来”。 就像我们今天了解 rune 的故事一样,在《Go语言进阶课》中,我们将一起探索更多 Go 语言的设计精髓与实践智慧,助你完成从“熟练”到“精通”的蜕变。

扫描下方二维码或点击[阅读原文],立即加入,开启你的 Go 语言精进之旅!

期待与你在极客时间相遇,共同探索 Go 语言的深层魅力!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

Go包构建:专家也未必了解的文件选择细节

本文永久链接 – https://tonybai.com/2024/11/21/go-source-file-selection-details-when-building-package

在Go语言开发中,包(package)是代码组织的基本单位,也是基本的构建单元。Go编译器会将每个包构建成一个目标文件(.a),然后通过链接器将这些目标文件链接在一起,形成最终的可执行程序。

尽管Go包的构建过程看似简单,但实际上蕴含着许多值得深入了解的细节。例如,当我们执行go build命令时,Go编译器是如何选择需要编译的源文件的?你可能会回答:“不就是通过文件名中的ARCH和OS标识以及构建约束(build constraints)来选择的吗?” 虽然你的答案并没有错,但如果我进一步提出以下问题,你是否还能给出确切的答案呢?

假设一个Go源文件使用了如下的构建约束:

//go:build unix

package foo
// ... ...

在执行GOOS=android go build时,这个文件是否会被编译?如果执行的是GOOS=aix go build呢?而“unix”究竟包含了哪些操作系统?

再进一步,当一个源文件的文件名中包含ARCH和操作系统标识,并且文件内容中也使用了构建约束时,Go编译器会如何处理这些信息的优先级?

即使是经验丰富的Go专家,对于上述在包构建过程中涉及的文件选择细节,可能也只能给出模糊的答案。

在实际开发中,我们常常需要针对不同操作系统和架构编写特定的代码,这意味着灵活性与复杂性并存。Go的构建约束和文件名约定虽然为我们提供了灵活性,但也带来了额外的复杂性。理解这些规则不仅有助于优化构建过程,还能有效避免潜在的错误和不必要的麻烦。

在这篇文章中,我将与大家探讨Go包构建过程中源文件选择的细节,包括文件名中ARCH和os标识约定和构建约束的作用,以及二者的优先级处理问题。希望通过这些内容,帮助开发者更好地掌握Go语言的构建机制,从而提高开发效率。

为了更好地说明Go包构建时的文件选择逻辑,我们先从Go包构建的一些“表象”说起。

注:在本文中,我们将使用Go 1.17引入的新版build constraints写法://go:build ,之前的// +build aix darwin dragonfly freebsd js,wasm …写法已经不再被推荐使用。如果你想对旧版build constraints写法有一个全面了解以便与新写法对比,推荐阅读我的《Go语言精进之路:从新手到高手的编程思想、方法和技巧》第2册

1. 表象

在Go工程中,通常一个目录对应一个Go包,每个Go包下可以存在多个以.go为后缀的Go源文件,这些源文件只能具有唯一的包名(测试源文件除外),以标准库fmt包为例,它的目录下的源文件列表如下(以Go 1.23.0源码为例):

$ls $GOROOT/src/fmt
doc.go              export_test.go          print.go            stringer_example_test.go
errors.go           fmt_test.go         scan.go             stringer_test.go
errors_test.go          format.go           scan_test.go
example_test.go         gostringer_example_test.go  state_test.go

在这些文件中,哪些最终进入到了fmt包的目标文件(fmt.a)中呢?贴心的Go工具链为我们提供了查看方法:

$go list -f '{{.GoFiles}}' fmt
[doc.go errors.go format.go print.go scan.go]

对于独立于目标ARCH和OS的fmt包来说,其Go源文件的选择似乎要简单一些。我们看到,除了包测试文件(xxx_test.go),其他文件都被编译到了最终的fmt包中。

我们再来看一个与目标ARCH和OS相关性较高的net包。除去子目录,这个包目录下的Go源文件数量大约有220多个,但在macOS/amd64下通过go list查看最终进入net包目标文件的文件,大约只有几十个:

$go list -f '{{.GoFiles}}' net
[addrselect.go cgo_darwin.go cgo_unix.go cgo_unix_syscall.go conf.go dial.go dnsclient.go dnsclient_unix.go dnsconfig.go dnsconfig_unix.go error_posix.go error_unix.go fd_posix.go fd_unix.go file.go file_unix.go hook.go hook_unix.go hosts.go interface.go interface_bsd.go interface_darwin.go ip.go iprawsock.go iprawsock_posix.go ipsock.go ipsock_posix.go lookup.go lookup_unix.go mac.go mptcpsock_stub.go net.go netcgo_off.go netgo_off.go nss.go parse.go pipe.go port.go port_unix.go rawconn.go rlimit_unix.go sendfile_unix_alt.go sock_bsd.go sock_posix.go sockaddr_posix.go sockopt_bsd.go sockopt_posix.go sockoptip_bsdvar.go sockoptip_posix.go splice_stub.go sys_cloexec.go tcpsock.go tcpsock_posix.go tcpsock_unix.go tcpsockopt_darwin.go tcpsockopt_posix.go udpsock.go udpsock_posix.go unixsock.go unixsock_posix.go unixsock_readmsg_cloexec.go writev_unix.go]

接下来,我们跳出Go标准库,来看一个自定义的示例:

$tree -F buildconstraints/demo1
buildconstraints/demo1
├── foo/
│   ├── f1_android.go
│   ├── f2_linux.go
│   └── f3_darwin.go
└── go.mod

// buildconstraints/demo1/foo/f1_android.go 

//go:build linux

package foo

func F1() {
}

// buildconstraints/demo1/foo/f2_linux.go
//go:build android

package foo

func F2() {
}

// buildconstraints/demo1/foo/f3_darwin.go
//go:build android

package foo

func F3() {
}

在GOOS=android下构建buildconstraints/demo1/foo这个包,哪些文件会被选出来呢,看下面输出结果:

$GOOS=android go list -f '{{.GoFiles}}' github.com/bigwhite/demo1/foo
[f1_android.go f2_linux.go]

如果说前两个示例还好理解,那这第三个示例很可能会让很多开发者觉得有些“发蒙”。 别急,上面三个示例都是表象,接下来,我们就来仔细探索一下Go构建时的文件选择机制。

2. 文件选择机制

Go包构建时选择源文件的机制还是蛮繁琐的,我们需要从源码入手梳理出其主要逻辑,在Go 1.23版本中,Go包构建过程源文件选择逻辑的代码位于\$GOROOT/src/go/build/build.go中,这个源文件有2k多行,不过不用担心,我这里会替你把主要调用逻辑梳理为下图:

函数Import调用Default.Import去获取包的详细信息,信息用build.Package结构表示:

// $GOROOT/src/go/build/build.go
// A Package describes the Go package found in a directory.
  type Package struct {
      Dir           string   // directory containing package sources
      Name          string   // package name
      ImportComment string   // path in import comment on package statement
      Doc           string   // documentation synopsis
      ImportPath    string   // import path of package ("" if unknown)
      Root          string   // root of Go tree where this package lives
      SrcRoot       string   // package source root directory ("" if unknown)
      PkgRoot       string   // package install root directory ("" if unknown)
      PkgTargetRoot string   // architecture dependent install root directory ("" if unknown)
      BinDir        string   // command install directory ("" if unknown)
      Goroot        bool     // package found in Go root
      PkgObj        string   // installed .a file
      AllTags       []string // tags that can influence file selection in this directory
      ConflictDir   string   // this directory shadows Dir in $GOPATH
      BinaryOnly    bool     // cannot be rebuilt from source (has //go:binary-only-package comment)

      // Source files
      GoFiles           []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
      ... ...

其中的GoFiles就是参与Go包编译的源文件列表。

Default是默认的上下文信息,包括构建所需的默认goenv中几个环境变量,比如GOARCH、GOOS等的值:

// Default is the default Context for builds.
// It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables
// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
var Default Context = defaultContext()

Context的Import方法代码行数很多,对于要了解文件选择细节的我们来说,其中最重要的调用是Context的matchFile方法。

matchFile正是那个用于确定某个Go源文件是否应该被选入最终包文件中的方法。它内部的逻辑可以分为两个主要步骤。

第一步是调用Context的goodOSArchFile方法对Go源文件的名字进行判定,goodOSArchFile方法的判定也有两个子步骤:

  • 判断名字中的OS和ARCH是否在Go支持的OS和ARCH列表中

当前Go支持的OS和ARCH在syslist.go文件中有定义:

// $GOROOT/src/go/build/syslist.go

// knownArch is the list of past, present, and future known GOARCH values.
// Do not remove from this list, as it is used for filename matching.
var knownArch = map[string]bool{
    "386":         true,
    "amd64":       true,
    "amd64p32":    true,
    "arm":         true,
    "armbe":       true,
    "arm64":       true,
    "arm64be":     true,
    "loong64":     true,
    "mips":        true,
    "mipsle":      true,
    "mips64":      true,
    "mips64le":    true,
    "mips64p32":   true,
    "mips64p32le": true,
    "ppc":         true,
    "ppc64":       true,
    "ppc64le":     true,
    "riscv":       true,
    "riscv64":     true,
    "s390":        true,
    "s390x":       true,
    "sparc":       true,
    "sparc64":     true,
    "wasm":        true,
}

// knownOS is the list of past, present, and future known GOOS values.
// Do not remove from this list, as it is used for filename matching.
// If you add an entry to this list, look at unixOS, below.
var knownOS = map[string]bool{
    "aix":       true,
    "android":   true,
    "darwin":    true,
    "dragonfly": true,
    "freebsd":   true,
    "hurd":      true,
    "illumos":   true,
    "ios":       true,
    "js":        true,
    "linux":     true,
    "nacl":      true,
    "netbsd":    true,
    "openbsd":   true,
    "plan9":     true,
    "solaris":   true,
    "wasip1":    true,
    "windows":   true,
    "zos":       true,
}

我们也可以通过下面命令查看:

$go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
freebsd/riscv64
illumos/amd64
ios/amd64
ios/arm64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/loong64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
openbsd/ppc64
openbsd/riscv64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
wasip1/wasm
windows/386
windows/amd64
windows/arm
windows/arm64

注:像sock_bsd.go、sock_posix.go这样的Go源文件,虽然它们的文件名中包含posix、bsd等字样,但这些文件实际上只是普通的Go源文件。其文件名本身并不会影响Go包在构建时选择文件的结果。

  • 调用matchTag来判定该Go源文件名字中的OS和ARCH是否与当前上下文信息中的OS和ARCH匹配

Go支持的源文件名组成格式如下:

  //  name_$(GOOS).*
  //  name_$(GOARCH).*
  //  name_$(GOOS)_$(GOARCH).*
  //  name_$(GOOS)_test.*
  //  name_$(GOARCH)_test.*
  //  name_$(GOOS)_$(GOARCH)_test.*

不过这里有三个例外,即:

如果上下文中的GOOS=android,那么文件名字中OS值为linux的Go源文件也算是匹配的;

如果上下文中的GOOS=illumos,那么文件名字中OS值为solaris的Go源文件也算是匹配的;

如果上下文中的GOOS=ios,那么文件名字中OS值为darwin的Go源文件也算是匹配的。

还有一个特殊处理,那就是当文件名字中OS值为unix时,该源文件可以匹配以下上下文中GOOS的值:

// $GOROOT/src/go/build/syslist.go

// unixOS is the set of GOOS values matched by the "unix" build tag.
// This is not used for filename matching.
// This list also appears in cmd/dist/build.go and
// cmd/go/internal/imports/build.go.
var unixOS = map[string]bool{
    "aix":       true,
    "android":   true,
    "darwin":    true,
    "dragonfly": true,
    "freebsd":   true,
    "hurd":      true,
    "illumos":   true,
    "ios":       true,
    "linux":     true,
    "netbsd":    true,
    "openbsd":   true,
    "solaris":   true,
}

这里面列出os都是所谓的“类Unix”操作系统。

如果goodOSArchFile方法返回文件名匹配成功,那么第二步就是调用Context的shouldBuild方法对Go源文件中的build constraints进行判定,这个判定过程也是调用matchTag完成的,因此规则与上面对matchTag的说明一致。如果判定match成功,那么该源文件将会被Go编译器编译到最终的Go包目标文件中去。

下面我们结合文章第一节“表象”中的那个自定义示例来判定一下为何最终会输出那个结果。

3. 示例分析

在buildconstraints/demo1/foo包目录中,一共有三个Go源文件:

$tree -F foo
foo
├── f1_android.go
├── f2_linux.go
└── f3_darwin.go

注意:当前我的系统为darwin/amd64,但我们使用了GOOS=android的环境变量。我们顺着上一节梳理出来的文件选择判定的主逻辑,对着三个文件逐一过一遍。

  • f1_android.go

首先用goodOSArchFile判定文件名是否匹配。当GOOS=android时,文件名中的os为android,文件名匹配成功,

然后用shouldBuild判定文件中的build constraints是否匹配。该文件的约束为linux,在上面matchTag的三个例外规则里提到过,当GOOS=android时,如果build constraints是linux,是可以匹配的。

因此,f1_android.go将出现在最终编译文件列表中。

  • f2_linux.go

首先用goodOSArchFile判定文件名是否匹配。当GOOS=android时,文件名中的os为linux,linux显然在go支持的os列表中,并且根据matchTag的例外规则,当GOOS=android时,文件名中的os为linux时是可以匹配的。

然后用shouldBuild判定文件中的build constraints是否匹配。该文件的约束为android,与GOOS相同,可以匹配。

因此,f2_linux.go将出现在最终编译文件列表中。

  • f3_darwin.go

首先用goodOSArchFile判定文件名是否匹配。当GOOS=android时,文件名中的os为darwin,虽然darwin在go支持的os列表中,但darwin与GOOS=android并不匹配,因此在goodOSArchFile这步中,f3_darwin.go就被“淘汰”掉了!即便f3_darwin.go中的build constraints为android。

因此,f3_darwin.go不会出现在最终编译文件列表中。

如果再增加一个源文件f4_unix.go,其内容为:

//go:build android

func F4() {
}

这个f4_unix.go是否会出现在最终的包编译文件列表中呢?这个作为思考题留给大家了,也欢迎你在评论区留言,说说你的思考结果。

4. 小结

在Go语言的开发过程中,包的构建是核心环节之一,而源文件的选择则是构建过程中一个复杂且关键的细节。本文深入探讨了Go编译器在执行go build命令时,如何根据文件名中的架构(ARCH)和操作系统(OS)标识,以及构建约束(build constraints),来选择需要编译的源文件。

通过具体示例,本文展示了不同文件名和构建约束如何影响最终的编译结果,并揭示了Go编译器处理这些信息的优先级。理解这些内部机制不仅能帮助开发者优化构建过程,还能有效避免潜在的错误。希望本文的分析能够给大家带去帮助。

注:限于篇幅,本文仅针对包编译文件选择最复杂的部分进行的探索,而像ReleaseTags(比如: go1.21等)、cgo、_test.go后缀等比较明显的约束并未涉及,同时对于新版build constraints的运算符组合也未提及,感兴趣的童鞋可以参考go build constraints官方文档查阅。

本文涉及的源码可以在这里下载。

5. 参考资料


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats